summaryrefslogtreecommitdiffstats
path: root/services/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'services/core/java')
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java194
-rw-r--r--services/core/java/com/android/server/BatteryService.java27
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java4
-rw-r--r--services/core/java/com/android/server/CommonTimeManagementService.java3
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java201
-rw-r--r--services/core/java/com/android/server/ConsumerIrService.java20
-rw-r--r--services/core/java/com/android/server/DiskStatsService.java1
-rw-r--r--services/core/java/com/android/server/EntropyMixer.java1
-rw-r--r--services/core/java/com/android/server/INativeDaemonConnectorCallbacks.java1
-rw-r--r--services/core/java/com/android/server/IdleMaintenanceService.java603
-rw-r--r--services/core/java/com/android/server/InputMethodManagerService.java229
-rw-r--r--services/core/java/com/android/server/LocationManagerService.java45
-rw-r--r--services/core/java/com/android/server/LockSettingsService.java3
-rw-r--r--services/core/java/com/android/server/MountService.java125
-rw-r--r--services/core/java/com/android/server/MountServiceIdler.java49
-rw-r--r--services/core/java/com/android/server/NativeDaemonConnector.java28
-rw-r--r--services/core/java/com/android/server/NetworkManagementService.java139
-rw-r--r--services/core/java/com/android/server/NsdService.java16
-rw-r--r--services/core/java/com/android/server/ServiceWatcher.java1
-rw-r--r--services/core/java/com/android/server/ShutdownActivity.java3
-rw-r--r--services/core/java/com/android/server/TextServicesManagerService.java1
-rw-r--r--services/core/java/com/android/server/UpdateLockService.java1
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java1
-rwxr-xr-xservices/core/java/com/android/server/am/ActiveServices.java8
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java299
-rw-r--r--services/core/java/com/android/server/am/ActivityRecord.java7
-rw-r--r--services/core/java/com/android/server/am/ActivityResult.java1
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityStack.java53
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java13
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java69
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java1
-rw-r--r--services/core/java/com/android/server/am/ContentProviderRecord.java1
-rw-r--r--services/core/java/com/android/server/am/IntentBindRecord.java2
-rw-r--r--services/core/java/com/android/server/am/PendingIntentRecord.java2
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java1
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java10
-rw-r--r--services/core/java/com/android/server/am/ProcessStatsService.java81
-rw-r--r--services/core/java/com/android/server/am/ReceiverList.java3
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java6
-rw-r--r--services/core/java/com/android/server/am/UsageStatsService.java2
-rw-r--r--services/core/java/com/android/server/clipboard/ClipboardService.java2
-rw-r--r--services/core/java/com/android/server/connectivity/PacManager.java3
-rw-r--r--services/core/java/com/android/server/connectivity/Tethering.java3
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java29
-rw-r--r--services/core/java/com/android/server/content/ContentService.java300
-rw-r--r--services/core/java/com/android/server/content/SyncManager.java1038
-rw-r--r--services/core/java/com/android/server/content/SyncOperation.java209
-rw-r--r--services/core/java/com/android/server/content/SyncQueue.java134
-rw-r--r--services/core/java/com/android/server/content/SyncStorageEngine.java1319
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java7
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java3
-rw-r--r--services/core/java/com/android/server/firewall/IntentFirewall.java1
-rw-r--r--services/core/java/com/android/server/firewall/SenderPackageFilter.java73
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java132
-rw-r--r--services/core/java/com/android/server/location/ComprehensiveCountryDetector.java1
-rw-r--r--services/core/java/com/android/server/location/GeocoderProxy.java1
-rw-r--r--services/core/java/com/android/server/location/GeofenceProxy.java2
-rw-r--r--services/core/java/com/android/server/location/GpsLocationProvider.java1
-rw-r--r--services/core/java/com/android/server/location/GpsXtraDownloader.java1
-rw-r--r--services/core/java/com/android/server/location/LocationFudger.java3
-rw-r--r--services/core/java/com/android/server/location/LocationProviderProxy.java1
-rw-r--r--services/core/java/com/android/server/location/LocationRequestStatistics.java205
-rw-r--r--services/core/java/com/android/server/location/MockProvider.java1
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java200
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java128
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsService.java12
-rw-r--r--services/core/java/com/android/server/notification/NotificationDelegate.java3
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java135
-rw-r--r--services/core/java/com/android/server/pm/KeySetManager.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageKeySetData.java2
-rwxr-xr-xservices/core/java/com/android/server/pm/PackageManagerService.java262
-rw-r--r--services/core/java/com/android/server/pm/PackageSettingBase.java3
-rw-r--r--services/core/java/com/android/server/pm/PersistentPreferredActivity.java96
-rw-r--r--services/core/java/com/android/server/pm/PersistentPreferredIntentResolver.java34
-rw-r--r--services/core/java/com/android/server/pm/SELinuxMMAC.java1
-rw-r--r--services/core/java/com/android/server/pm/Settings.java132
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java70
-rw-r--r--services/core/java/com/android/server/power/AutomaticBrightnessController.java687
-rw-r--r--services/core/java/com/android/server/power/DisplayPowerController.java575
-rw-r--r--services/core/java/com/android/server/power/DisplayPowerRequest.java9
-rw-r--r--services/core/java/com/android/server/power/Notifier.java11
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java51
-rw-r--r--services/core/java/com/android/server/search/SearchManagerService.java2
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java28
-rw-r--r--services/core/java/com/android/server/storage/DeviceStorageMonitorService.java1
-rw-r--r--services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java4
-rw-r--r--services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java2
-rw-r--r--services/core/java/com/android/server/updates/TZInfoInstallReceiver.java1
-rw-r--r--services/core/java/com/android/server/wifi/README.txt19
-rw-r--r--services/core/java/com/android/server/wifi/WifiController.java751
-rw-r--r--services/core/java/com/android/server/wifi/WifiNotificationController.java297
-rw-r--r--services/core/java/com/android/server/wifi/WifiService.java1599
-rw-r--r--services/core/java/com/android/server/wifi/WifiSettingsStore.java197
-rw-r--r--services/core/java/com/android/server/wifi/WifiTrafficPoller.java191
-rw-r--r--services/core/java/com/android/server/wm/DragState.java1
-rw-r--r--services/core/java/com/android/server/wm/FakeWindowImpl.java2
-rw-r--r--services/core/java/com/android/server/wm/KeyguardDisableHandler.java1
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java106
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java10
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java5
100 files changed, 5747 insertions, 5607 deletions
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index c14ed8b..f03a8e0 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -39,8 +39,10 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.WorkSource;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Pair;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.TimeUtils;
import java.io.ByteArrayOutputStream;
@@ -53,9 +55,7 @@ import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
-import java.util.HashMap;
import java.util.LinkedList;
-import java.util.Map;
import java.util.TimeZone;
import static android.app.AlarmManager.RTC_WAKEUP;
@@ -313,7 +313,22 @@ class AlarmManagerService extends SystemService {
return 0;
}
}
-
+
+ final Comparator<Alarm> mAlarmDispatchComparator = new Comparator<Alarm>() {
+ @Override
+ public int compare(Alarm lhs, Alarm rhs) {
+ if (lhs.wakeup != rhs.wakeup) {
+ return lhs.wakeup ? -1 : 1;
+ }
+ if (lhs.whenElapsed < rhs.whenElapsed) {
+ return -1;
+ } else if (lhs.whenElapsed > rhs.whenElapsed) {
+ return 1;
+ }
+ return 0;
+ }
+ };
+
// minimum recurrence period or alarm futurity for us to be able to fuzz it
static final long MIN_FUZZABLE_INTERVAL = 10000;
static final BatchTimeOrder sBatchOrder = new BatchTimeOrder();
@@ -446,6 +461,7 @@ class AlarmManagerService extends SystemService {
}
static final class BroadcastStats {
+ final int mUid;
final String mPackageName;
long aggregateTime;
@@ -453,16 +469,17 @@ class AlarmManagerService extends SystemService {
int numWakeup;
long startTime;
int nesting;
- final HashMap<Pair<String, ComponentName>, FilterStats> filterStats
- = new HashMap<Pair<String, ComponentName>, FilterStats>();
+ final ArrayMap<Pair<String, ComponentName>, FilterStats> filterStats
+ = new ArrayMap<Pair<String, ComponentName>, FilterStats>();
- BroadcastStats(String packageName) {
+ BroadcastStats(int uid, String packageName) {
+ mUid = uid;
mPackageName = packageName;
}
}
- final HashMap<String, BroadcastStats> mBroadcastStats
- = new HashMap<String, BroadcastStats>();
+ final SparseArray<ArrayMap<String, BroadcastStats>> mBroadcastStats
+ = new SparseArray<ArrayMap<String, BroadcastStats>>();
@Override
public void onStart() {
@@ -474,8 +491,8 @@ class AlarmManagerService extends SystemService {
setTimeZoneImpl(SystemProperties.get(TIMEZONE_PROPERTY));
PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*alarm*");
+
mTimeTickSender = PendingIntent.getBroadcastAsUser(getContext(), 0,
new Intent(Intent.ACTION_TIME_TICK).addFlags(
Intent.FLAG_RECEIVER_REGISTERED_ONLY
@@ -754,24 +771,26 @@ class AlarmManagerService extends SystemService {
}
};
int len = 0;
- for (Map.Entry<String, BroadcastStats> be : mBroadcastStats.entrySet()) {
- BroadcastStats bs = be.getValue();
- for (Map.Entry<Pair<String, ComponentName>, FilterStats> fe
- : bs.filterStats.entrySet()) {
- FilterStats fs = fe.getValue();
- int pos = len > 0
- ? Arrays.binarySearch(topFilters, 0, len, fs, comparator) : 0;
- if (pos < 0) {
- pos = -pos - 1;
- }
- if (pos < topFilters.length) {
- int copylen = topFilters.length - pos - 1;
- if (copylen > 0) {
- System.arraycopy(topFilters, pos, topFilters, pos+1, copylen);
+ for (int iu=0; iu<mBroadcastStats.size(); iu++) {
+ ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(iu);
+ for (int ip=0; ip<uidStats.size(); ip++) {
+ BroadcastStats bs = uidStats.valueAt(ip);
+ for (int is=0; is<bs.filterStats.size(); is++) {
+ FilterStats fs = bs.filterStats.valueAt(is);
+ int pos = len > 0
+ ? Arrays.binarySearch(topFilters, 0, len, fs, comparator) : 0;
+ if (pos < 0) {
+ pos = -pos - 1;
}
- topFilters[pos] = fs;
- if (len < topFilters.length) {
- len++;
+ if (pos < topFilters.length) {
+ int copylen = topFilters.length - pos - 1;
+ if (copylen > 0) {
+ System.arraycopy(topFilters, pos, topFilters, pos+1, copylen);
+ }
+ topFilters[pos] = fs;
+ if (len < topFilters.length) {
+ len++;
+ }
}
}
}
@@ -785,7 +804,8 @@ class AlarmManagerService extends SystemService {
TimeUtils.formatDuration(fs.aggregateTime, pw);
pw.print(" running, "); pw.print(fs.numWakeup);
pw.print(" wakeups, "); pw.print(fs.count);
- pw.print(" alarms: "); pw.print(fs.mBroadcastStats.mPackageName);
+ pw.print(" alarms: "); UserHandle.formatUid(pw, fs.mBroadcastStats.mUid);
+ pw.print(":"); pw.print(fs.mBroadcastStats.mPackageName);
pw.println();
pw.print(" ");
if (fs.mTarget.first != null) {
@@ -801,35 +821,39 @@ class AlarmManagerService extends SystemService {
pw.println(" ");
pw.println(" Alarm Stats:");
final ArrayList<FilterStats> tmpFilters = new ArrayList<FilterStats>();
- for (Map.Entry<String, BroadcastStats> be : mBroadcastStats.entrySet()) {
- BroadcastStats bs = be.getValue();
- pw.print(" ");
- if (bs.nesting > 0) pw.print("*ACTIVE* ");
- pw.print(be.getKey());
- pw.print(" "); TimeUtils.formatDuration(bs.aggregateTime, pw);
- pw.print(" running, "); pw.print(bs.numWakeup);
- pw.println(" wakeups:");
- tmpFilters.clear();
- for (Map.Entry<Pair<String, ComponentName>, FilterStats> fe
- : bs.filterStats.entrySet()) {
- tmpFilters.add(fe.getValue());
- }
- Collections.sort(tmpFilters, comparator);
- for (int i=0; i<tmpFilters.size(); i++) {
- FilterStats fs = tmpFilters.get(i);
- pw.print(" ");
- if (fs.nesting > 0) pw.print("*ACTIVE* ");
- TimeUtils.formatDuration(fs.aggregateTime, pw);
- pw.print(" "); pw.print(fs.numWakeup);
- pw.print(" wakes " ); pw.print(fs.count);
- pw.print(" alarms:");
- if (fs.mTarget.first != null) {
- pw.print(" act="); pw.print(fs.mTarget.first);
- }
- if (fs.mTarget.second != null) {
- pw.print(" cmp="); pw.print(fs.mTarget.second.toShortString());
- }
- pw.println();
+ for (int iu=0; iu<mBroadcastStats.size(); iu++) {
+ ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(iu);
+ for (int ip=0; ip<uidStats.size(); ip++) {
+ BroadcastStats bs = uidStats.valueAt(ip);
+ pw.print(" ");
+ if (bs.nesting > 0) pw.print("*ACTIVE* ");
+ UserHandle.formatUid(pw, bs.mUid);
+ pw.print(":");
+ pw.print(bs.mPackageName);
+ pw.print(" "); TimeUtils.formatDuration(bs.aggregateTime, pw);
+ pw.print(" running, "); pw.print(bs.numWakeup);
+ pw.println(" wakeups:");
+ tmpFilters.clear();
+ for (int is=0; is<bs.filterStats.size(); is++) {
+ tmpFilters.add(bs.filterStats.valueAt(is));
+ }
+ Collections.sort(tmpFilters, comparator);
+ for (int i=0; i<tmpFilters.size(); i++) {
+ FilterStats fs = tmpFilters.get(i);
+ pw.print(" ");
+ if (fs.nesting > 0) pw.print("*ACTIVE* ");
+ TimeUtils.formatDuration(fs.aggregateTime, pw);
+ pw.print(" "); pw.print(fs.numWakeup);
+ pw.print(" wakes " ); pw.print(fs.count);
+ pw.print(" alarms:");
+ if (fs.mTarget.first != null) {
+ pw.print(" act="); pw.print(fs.mTarget.first);
+ }
+ if (fs.mTarget.second != null) {
+ pw.print(" cmp="); pw.print(fs.mTarget.second.toShortString());
+ }
+ pw.println();
+ }
}
}
@@ -1049,7 +1073,8 @@ class AlarmManagerService extends SystemService {
private native int setKernelTime(long nativeData, long millis);
private native int setKernelTimezone(long nativeData, int minuteswest);
- void triggerAlarmsLocked(ArrayList<Alarm> triggerList, long nowELAPSED, long nowRTC) {
+ void triggerAlarmsLocked(ArrayList<Alarm> triggerList, final long nowELAPSED,
+ final long nowRTC) {
// batches are temporally sorted, so we need only pull from the
// start of the list until we either empty it or hit a batch
// that is not yet deliverable
@@ -1088,6 +1113,14 @@ class AlarmManagerService extends SystemService {
}
}
+
+ Collections.sort(triggerList, mAlarmDispatchComparator);
+
+ if (localLOGV) {
+ for (int i=0; i<triggerList.size(); i++) {
+ Slog.v(TAG, "Triggering alarm #" + i + ": " + triggerList.get(i));
+ }
+ }
}
/**
@@ -1108,7 +1141,8 @@ class AlarmManagerService extends SystemService {
}
private static class Alarm {
- public int type;
+ public final int type;
+ public final boolean wakeup;
public int count;
public long when;
public long windowLength;
@@ -1121,6 +1155,8 @@ class AlarmManagerService extends SystemService {
public Alarm(int _type, long _when, long _whenElapsed, long _windowLength, long _maxWhen,
long _interval, PendingIntent _op, WorkSource _ws) {
type = _type;
+ wakeup = _type == AlarmManager.ELAPSED_REALTIME_WAKEUP
+ || _type == AlarmManager.RTC_WAKEUP;
when = _when;
whenElapsed = _whenElapsed;
windowLength = _windowLength;
@@ -1138,6 +1174,8 @@ class AlarmManagerService extends SystemService {
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(" type ");
sb.append(type);
+ sb.append(" when ");
+ sb.append(when);
sb.append(" ");
sb.append(operation.getTargetPackage());
sb.append('}');
@@ -1243,6 +1281,9 @@ class AlarmManagerService extends SystemService {
// we have an active broadcast so stay awake.
if (mBroadcastRefCount == 0) {
setWakelockWorkSource(alarm.operation, alarm.workSource);
+ mWakeLock.setUnimportantForLogging(
+ alarm.operation == mTimeTickSender);
+ mWakeLock.setHistoryTag(alarm.operation.getTag("*alarm*:"));
mWakeLock.acquire();
}
final InFlight inflight = new InFlight(AlarmManagerService.this,
@@ -1270,8 +1311,16 @@ class AlarmManagerService extends SystemService {
|| alarm.type == RTC_WAKEUP) {
bs.numWakeup++;
fs.numWakeup++;
- ActivityManagerNative.noteWakeupAlarm(
- alarm.operation);
+ if (alarm.workSource != null && alarm.workSource.size() > 0) {
+ for (int wi=0; wi<alarm.workSource.size(); wi++) {
+ ActivityManagerNative.noteWakeupAlarm(
+ alarm.operation, alarm.workSource.get(wi),
+ alarm.workSource.getName(wi));
+ }
+ } else {
+ ActivityManagerNative.noteWakeupAlarm(
+ alarm.operation, -1, null);
+ }
}
} catch (PendingIntent.CanceledException e) {
if (alarm.repeatInterval > 0) {
@@ -1454,7 +1503,14 @@ class AlarmManagerService extends SystemService {
if (pkgList != null && (pkgList.length > 0)) {
for (String pkg : pkgList) {
removeLocked(pkg);
- mBroadcastStats.remove(pkg);
+ for (int i=mBroadcastStats.size()-1; i>=0; i--) {
+ ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(i);
+ if (uidStats.remove(pkg) != null) {
+ if (uidStats.size() <= 0) {
+ mBroadcastStats.removeAt(i);
+ }
+ }
+ }
}
}
}
@@ -1462,11 +1518,17 @@ class AlarmManagerService extends SystemService {
}
private final BroadcastStats getStatsLocked(PendingIntent pi) {
- String pkg = pi.getTargetPackage();
- BroadcastStats bs = mBroadcastStats.get(pkg);
+ String pkg = pi.getCreatorPackage();
+ int uid = pi.getCreatorUid();
+ ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.get(uid);
+ if (uidStats == null) {
+ uidStats = new ArrayMap<String, BroadcastStats>();
+ mBroadcastStats.put(uid, uidStats);
+ }
+ BroadcastStats bs = uidStats.get(pkg);
if (bs == null) {
- bs = new BroadcastStats(pkg);
- mBroadcastStats.put(pkg, bs);
+ bs = new BroadcastStats(uid, pkg);
+ uidStats.put(pkg, bs);
}
return bs;
}
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index cc9055d..cb5946a 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -108,6 +108,7 @@ public final class BatteryService extends Binder {
private final Object mLock = new Object();
private BatteryProperties mBatteryProps;
+ private final BatteryProperties mLastBatteryProps = new BatteryProperties();
private boolean mBatteryLevelCritical;
private int mLastBatteryStatus;
private int mLastBatteryHealth;
@@ -157,7 +158,7 @@ public final class BatteryService extends Binder {
"DEVPATH=/devices/virtual/switch/invalid_charger");
}
- IBinder b = ServiceManager.getService("batterypropreg");
+ IBinder b = ServiceManager.getService("batteryproperties");
final IBatteryPropertiesRegistrar batteryPropertiesRegistrar =
IBatteryPropertiesRegistrar.Stub.asInterface(b);
try {
@@ -281,6 +282,8 @@ public final class BatteryService extends Binder {
mBatteryProps = props;
// Process the new values.
processValuesLocked();
+ } else {
+ mLastBatteryProps.set(props);
}
}
}
@@ -311,8 +314,6 @@ public final class BatteryService extends Binder {
+ ", batteryLevel=" + mBatteryProps.batteryLevel
+ ", batteryTechnology=" + mBatteryProps.batteryTechnology
+ ", batteryVoltage=" + mBatteryProps.batteryVoltage
- + ", batteryCurrentNow=" + mBatteryProps.batteryCurrentNow
- + ", batteryChargeCounter=" + mBatteryProps.batteryChargeCounter
+ ", batteryTemperature=" + mBatteryProps.batteryTemperature
+ ", mBatteryLevelCritical=" + mBatteryLevelCritical
+ ", mPlugType=" + mPlugType);
@@ -613,21 +614,15 @@ public final class BatteryService extends Binder {
pw.println(" level: " + mBatteryProps.batteryLevel);
pw.println(" scale: " + BATTERY_SCALE);
pw.println(" voltage: " + mBatteryProps.batteryVoltage);
-
- if (mBatteryProps.batteryCurrentNow != Integer.MIN_VALUE) {
- pw.println(" current now: " + mBatteryProps.batteryCurrentNow);
- }
-
- if (mBatteryProps.batteryChargeCounter != Integer.MIN_VALUE) {
- pw.println(" charge counter: " + mBatteryProps.batteryChargeCounter);
- }
-
pw.println(" temperature: " + mBatteryProps.batteryTemperature);
pw.println(" technology: " + mBatteryProps.batteryTechnology);
} else if (args.length == 3 && "set".equals(args[0])) {
String key = args[1];
String value = args[2];
try {
+ if (!mUpdatesStopped) {
+ mLastBatteryProps.set(mBatteryProps);
+ }
boolean update = true;
if ("ac".equals(key)) {
mBatteryProps.chargerAcOnline = Integer.parseInt(value) != 0;
@@ -660,13 +655,17 @@ public final class BatteryService extends Binder {
} else if (args.length == 1 && "reset".equals(args[0])) {
long ident = Binder.clearCallingIdentity();
try {
- mUpdatesStopped = false;
+ if (mUpdatesStopped) {
+ mUpdatesStopped = false;
+ mBatteryProps.set(mLastBatteryProps);
+ processValuesLocked();
+ }
} finally {
Binder.restoreCallingIdentity(ident);
}
} else {
pw.println("Dump current battery state, or:");
- pw.println(" set ac|usb|wireless|status|level|invalid <value>");
+ pw.println(" set [ac|usb|wireless|status|level|invalid] <value>");
pw.println(" reset");
}
}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 546324a..0d6f548 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -1005,7 +1005,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
mState = BluetoothAdapter.STATE_OFF;
// enable
handleEnable(mQuietEnable);
- } else if (mBinding || mBluetooth != null) {
+ } else if (mBinding || mBluetooth != null) {
Message userMsg = mHandler.obtainMessage(MESSAGE_USER_SWITCHED);
userMsg.arg2 = 1 + msg.arg2;
// if user is switched when service is being binding
@@ -1014,7 +1014,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
if (DBG) {
Log.d(TAG, "delay MESSAGE_USER_SWITCHED " + userMsg.arg2);
}
- }
+ }
break;
}
}
diff --git a/services/core/java/com/android/server/CommonTimeManagementService.java b/services/core/java/com/android/server/CommonTimeManagementService.java
index 710fb9d..60b366a 100644
--- a/services/core/java/com/android/server/CommonTimeManagementService.java
+++ b/services/core/java/com/android/server/CommonTimeManagementService.java
@@ -18,7 +18,6 @@ package com.android.server;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.net.InetAddress;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -26,10 +25,8 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
-import android.net.IConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.InterfaceConfiguration;
-import android.net.NetworkInfo;
import android.os.Binder;
import android.os.CommonTimeConfig;
import android.os.Handler;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 8d158cf..ffc748f 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -33,6 +33,7 @@ import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import android.app.AlarmManager;
+import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -44,7 +45,9 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -114,6 +117,7 @@ import com.android.internal.net.VpnProfile;
import com.android.internal.telephony.DctConstants;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;
@@ -144,7 +148,6 @@ import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.URL;
-import java.net.URLConnection;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -414,6 +417,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private SettingsObserver mSettingsObserver;
+ private AppOpsManager mAppOpsManager;
+
NetworkConfig[] mNetConfigs;
int mNetworksDefined;
@@ -697,6 +702,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
filter = new IntentFilter();
filter.addAction(CONNECTED_TO_PROVISIONING_NETWORK_ACTION);
mContext.registerReceiver(mProvisioningReceiver, filter);
+
+ mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
}
/**
@@ -1531,6 +1538,40 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
/**
+ * Check if the address falls into any of currently running VPN's route's.
+ */
+ private boolean isAddressUnderVpn(InetAddress address) {
+ synchronized (mVpns) {
+ synchronized (mRoutesLock) {
+ int uid = UserHandle.getCallingUserId();
+ Vpn vpn = mVpns.get(uid);
+ if (vpn == null) {
+ return false;
+ }
+
+ // Check if an exemption exists for this address.
+ for (LinkAddress destination : mExemptAddresses) {
+ if (!NetworkUtils.addressTypeMatches(address, destination.getAddress())) {
+ continue;
+ }
+
+ int prefix = destination.getNetworkPrefixLength();
+ InetAddress addrMasked = NetworkUtils.getNetworkPart(address, prefix);
+ InetAddress destMasked = NetworkUtils.getNetworkPart(destination.getAddress(),
+ prefix);
+
+ if (addrMasked.equals(destMasked)) {
+ return false;
+ }
+ }
+
+ // Finally check if the address is covered by the VPN.
+ return vpn.isAddressCovered(address);
+ }
+ }
+ }
+
+ /**
* @deprecated use requestRouteToHostAddress instead
*
* Ensure that a network route exists to deliver traffic to the specified
@@ -1541,14 +1582,14 @@ public class ConnectivityService extends IConnectivityManager.Stub {
* desired
* @return {@code true} on success, {@code false} on failure
*/
- public boolean requestRouteToHost(int networkType, int hostAddress) {
+ public boolean requestRouteToHost(int networkType, int hostAddress, String packageName) {
InetAddress inetAddress = NetworkUtils.intToInetAddress(hostAddress);
if (inetAddress == null) {
return false;
}
- return requestRouteToHostAddress(networkType, inetAddress.getAddress());
+ return requestRouteToHostAddress(networkType, inetAddress.getAddress(), packageName);
}
/**
@@ -1560,11 +1601,40 @@ public class ConnectivityService extends IConnectivityManager.Stub {
* desired
* @return {@code true} on success, {@code false} on failure
*/
- public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) {
+ public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress,
+ String packageName) {
enforceChangePermission();
if (mProtectedNetworks.contains(networkType)) {
enforceConnectivityInternalPermission();
}
+ boolean exempt;
+ InetAddress addr;
+ try {
+ addr = InetAddress.getByAddress(hostAddress);
+ } catch (UnknownHostException e) {
+ if (DBG) log("requestRouteToHostAddress got " + e.toString());
+ return false;
+ }
+ // System apps may request routes bypassing the VPN to keep other networks working.
+ if (Binder.getCallingUid() == Process.SYSTEM_UID) {
+ exempt = true;
+ } else {
+ mAppOpsManager.checkPackage(Binder.getCallingUid(), packageName);
+ try {
+ ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(packageName,
+ 0);
+ exempt = (info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ } catch (NameNotFoundException e) {
+ throw new IllegalArgumentException("Failed to find calling package details", e);
+ }
+ }
+
+ // Non-exempt routeToHost's can only be added if the host is not covered by the VPN.
+ // This can be either because the VPN's routes do not cover the destination or a
+ // system application added an exemption that covers this destination.
+ if (!exempt && isAddressUnderVpn(addr)) {
+ return false;
+ }
if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
if (DBG) log("requestRouteToHostAddress on invalid network: " + networkType);
@@ -1591,18 +1661,13 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
final long token = Binder.clearCallingIdentity();
try {
- InetAddress addr = InetAddress.getByAddress(hostAddress);
LinkProperties lp = tracker.getLinkProperties();
- boolean ok = addRouteToAddress(lp, addr, EXEMPT);
+ boolean ok = addRouteToAddress(lp, addr, exempt);
if (DBG) log("requestRouteToHostAddress ok=" + ok);
return ok;
- } catch (UnknownHostException e) {
- if (DBG) log("requestRouteToHostAddress got " + e.toString());
} finally {
Binder.restoreCallingIdentity(token);
}
- if (DBG) log("requestRouteToHostAddress X bottom return false");
- return false;
}
private boolean addRoute(LinkProperties p, RouteInfo r, boolean toDefaultTable,
@@ -1921,7 +1986,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mNetTrackers[prevNetType].setTeardownRequested(false);
// Remove idletimer previously setup in {@code handleConnect}
- removeDataActivityTracking(prevNetType);
+ if (mNetConfigs[prevNetType].isDefault()) {
+ removeDataActivityTracking(prevNetType);
+ }
/*
* If the disconnected network is not the active one, then don't report
@@ -2253,8 +2320,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private void handleConnect(NetworkInfo info) {
final int newNetType = info.getType();
- setupDataActivityTracking(newNetType);
-
// snapshot isFailover, because sendConnectedBroadcast() resets it
boolean isFailover = info.isFailover();
final NetworkStateTracker thisNet = mNetTrackers[newNetType];
@@ -2292,6 +2357,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return;
}
}
+ setupDataActivityTracking(newNetType);
synchronized (ConnectivityService.this) {
// have a new default network, release the transition wakelock in a second
// if it's held. The second pause is to allow apps to reconnect over the
@@ -2312,9 +2378,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mInetConditionChangeInFlight = false;
// Don't do this - if we never sign in stay, grey
//reportNetworkCondition(mActiveDefaultNetwork, 100);
+ updateNetworkSettings(thisNet);
}
thisNet.setTeardownRequested(false);
- updateNetworkSettings(thisNet);
updateMtuSizeSettings(thisNet);
handleConnectivityChange(newNetType, false);
sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay());
@@ -2329,36 +2395,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
- private void handleCaptivePortalTrackerCheck(NetworkInfo info) {
- if (DBG) log("Captive portal check " + info);
- int type = info.getType();
- final NetworkStateTracker thisNet = mNetTrackers[type];
- if (mNetConfigs[type].isDefault()) {
- if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != type) {
- if (isNewNetTypePreferredOverCurrentNetType(type)) {
- if (DBG) log("Captive check on " + info.getTypeName());
- mCaptivePortalTracker.detectCaptivePortal(new NetworkInfo(info));
- return;
- } else {
- if (DBG) log("Tear down low priority net " + info.getTypeName());
- teardown(thisNet);
- return;
- }
- }
- }
-
- if (DBG) log("handleCaptivePortalTrackerCheck: call captivePortalCheckComplete ni=" + info);
- thisNet.captivePortalCheckComplete();
- }
-
- /** @hide */
- @Override
- public void captivePortalCheckComplete(NetworkInfo info) {
- enforceConnectivityInternalPermission();
- if (DBG) log("captivePortalCheckComplete: ni=" + info);
- mNetTrackers[info.getType()].captivePortalCheckComplete();
- }
-
/** @hide */
@Override
public void captivePortalCheckCompleted(NetworkInfo info, boolean isCaptivePortal) {
@@ -2371,7 +2407,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
* Setup data activity tracking for the given network interface.
*
* Every {@code setupDataActivityTracking} should be paired with a
- * {@link removeDataActivityTracking} for cleanup.
+ * {@link #removeDataActivityTracking} for cleanup.
*/
private void setupDataActivityTracking(int type) {
final NetworkStateTracker thisNet = mNetTrackers[type];
@@ -2382,7 +2418,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (ConnectivityManager.isNetworkTypeMobile(type)) {
timeout = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE,
- 0);
+ 5);
// Canonicalize mobile network type
type = ConnectivityManager.TYPE_MOBILE;
} else if (ConnectivityManager.TYPE_WIFI == type) {
@@ -2396,7 +2432,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (timeout > 0 && iface != null) {
try {
- mNetd.addIdleTimer(iface, timeout, Integer.toString(type));
+ mNetd.addIdleTimer(iface, timeout, type);
} catch (RemoteException e) {
}
}
@@ -2469,9 +2505,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
"\n car=" + car);
}
} else {
- if (DBG) {
- log("handleConnectivityChange: address are the same reset per doReset" +
- " linkProperty[" + netType + "]:" +
+ if (VDBG) {
+ log("handleConnectivityChange: addresses are the same reset per" +
+ " doReset linkProperty[" + netType + "]:" +
" resetMask=" + resetMask);
}
}
@@ -2662,7 +2698,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
final int mtu = nt.getLinkProperties().getMtu();
if (mtu < 68 || mtu > 10000) {
- loge("Unexpected mtu value: " + nt);
+ loge("Unexpected mtu value: " + mtu + ", " + nt);
return;
}
@@ -2699,6 +2735,15 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
setBufferSize(bufferSizes);
}
+
+ final String defaultRwndKey = "net.tcp.default_init_rwnd";
+ int defaultRwndValue = SystemProperties.getInt(defaultRwndKey, 0);
+ Integer rwndValue = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.TCP_DEFAULT_INIT_RWND, defaultRwndValue);
+ final String sysctlKey = "sys.sysctl.tcp_def_init_rwnd";
+ if (rwndValue != 0) {
+ SystemProperties.set(sysctlKey, rwndValue.toString());
+ }
}
/**
@@ -2900,6 +2945,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
+ pw.print("Active default network: "); pw.println(getNetworkTypeName(mActiveDefaultNetwork));
+ pw.println();
+
pw.println("Network Requester Pids:");
pw.increaseIndent();
for (int net : mPriorityList) {
@@ -2985,9 +3033,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (info.getDetailedState() ==
NetworkInfo.DetailedState.FAILED) {
handleConnectionFailure(info);
- } else if (info.getDetailedState() ==
- DetailedState.CAPTIVE_PORTAL_CHECK) {
- handleCaptivePortalTrackerCheck(info);
} else if (info.isConnectedToProvisioningNetwork()) {
/**
* TODO: Create ConnectivityManager.TYPE_MOBILE_PROVISIONING
@@ -3041,7 +3086,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED: {
info = (NetworkInfo) msg.obj;
int type = info.getType();
- updateNetworkSettings(mNetTrackers[type]);
+ if (mNetConfigs[type].isDefault()) updateNetworkSettings(mNetTrackers[type]);
break;
}
}
@@ -3600,8 +3645,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
int user = UserHandle.getUserId(Binder.getCallingUid());
if (ConnectivityManager.isNetworkTypeValid(type) && mNetTrackers[type] != null) {
synchronized(mVpns) {
- mVpns.get(user).protect(socket,
- mNetTrackers[type].getLinkProperties().getInterfaceName());
+ mVpns.get(user).protect(socket);
}
return true;
}
@@ -4358,7 +4402,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
// Make a route to host so we check the specific interface.
if (mCs.requestRouteToHostAddress(ConnectivityManager.TYPE_MOBILE_HIPRI,
- hostAddr.getAddress())) {
+ hostAddr.getAddress(), null)) {
// Wait a short time to be sure the route is established ??
log("isMobileOk:"
+ " wait to establish route to hostAddr=" + hostAddr);
@@ -4528,10 +4572,13 @@ public class ConnectivityService extends IConnectivityManager.Stub {
* @param seconds
*/
private static void sleep(int seconds) {
- try {
- Thread.sleep(seconds * 1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
+ long stopTime = System.nanoTime() + (seconds * 1000000000);
+ long sleepTime;
+ while ((sleepTime = stopTime - System.nanoTime()) > 0) {
+ try {
+ Thread.sleep(sleepTime / 1000000);
+ } catch (InterruptedException ignored) {
+ }
}
}
@@ -4567,15 +4614,27 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mdst.enableMobileProvisioning(url);
} else {
if (DBG) log("handleMobileProvisioningAction: on default network");
- Intent newIntent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN,
- Intent.CATEGORY_APP_BROWSER);
- newIntent.setData(Uri.parse(url));
- newIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
- Intent.FLAG_ACTIVITY_NEW_TASK);
- try {
- mContext.startActivity(newIntent);
- } catch (ActivityNotFoundException e) {
- loge("handleMobileProvisioningAction: startActivity failed" + e);
+ // Check for apps that can handle provisioning first
+ Intent provisioningIntent = new Intent(TelephonyIntents.ACTION_CARRIER_SETUP);
+ provisioningIntent.addCategory(TelephonyIntents.CATEGORY_MCCMNC_PREFIX
+ + mTelephonyManager.getSimOperator());
+ if (mContext.getPackageManager().resolveActivity(provisioningIntent, 0 /* flags */)
+ != null) {
+ provisioningIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
+ Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(provisioningIntent);
+ } else {
+ // If no apps exist, use standard URL ACTION_VIEW method
+ Intent newIntent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN,
+ Intent.CATEGORY_APP_BROWSER);
+ newIntent.setData(Uri.parse(url));
+ newIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
+ Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ mContext.startActivity(newIntent);
+ } catch (ActivityNotFoundException e) {
+ loge("handleMobileProvisioningAction: startActivity failed" + e);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/ConsumerIrService.java b/services/core/java/com/android/server/ConsumerIrService.java
index 583f1bc..ea6812d 100644
--- a/services/core/java/com/android/server/ConsumerIrService.java
+++ b/services/core/java/com/android/server/ConsumerIrService.java
@@ -16,33 +16,13 @@
package com.android.server;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.PackageManager;
-import android.database.ContentObserver;
-import android.hardware.input.InputManager;
import android.hardware.IConsumerIrService;
-import android.os.Handler;
import android.os.PowerManager;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.IBinder;
-import android.os.Binder;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.os.WorkSource;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
import android.util.Slog;
-import android.view.InputDevice;
import java.lang.RuntimeException;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.ListIterator;
public class ConsumerIrService extends IConsumerIrService.Stub {
private static final String TAG = "ConsumerIrService";
diff --git a/services/core/java/com/android/server/DiskStatsService.java b/services/core/java/com/android/server/DiskStatsService.java
index ac25dc5..bc12fc5 100644
--- a/services/core/java/com/android/server/DiskStatsService.java
+++ b/services/core/java/com/android/server/DiskStatsService.java
@@ -19,7 +19,6 @@ package com.android.server;
import android.content.Context;
import android.os.Binder;
import android.os.Environment;
-import android.os.FileUtils;
import android.os.StatFs;
import android.os.SystemClock;
diff --git a/services/core/java/com/android/server/EntropyMixer.java b/services/core/java/com/android/server/EntropyMixer.java
index cfdbf7d..24d8d1e 100644
--- a/services/core/java/com/android/server/EntropyMixer.java
+++ b/services/core/java/com/android/server/EntropyMixer.java
@@ -20,7 +20,6 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.OutputStream;
import java.io.PrintWriter;
import android.content.BroadcastReceiver;
diff --git a/services/core/java/com/android/server/INativeDaemonConnectorCallbacks.java b/services/core/java/com/android/server/INativeDaemonConnectorCallbacks.java
index 6fbf713..0cf9dcd 100644
--- a/services/core/java/com/android/server/INativeDaemonConnectorCallbacks.java
+++ b/services/core/java/com/android/server/INativeDaemonConnectorCallbacks.java
@@ -20,5 +20,6 @@ package com.android.server;
interface INativeDaemonConnectorCallbacks {
void onDaemonConnected();
+ boolean onCheckHoldWakeLock(int code);
boolean onEvent(int code, String raw, String[] cooked);
}
diff --git a/services/core/java/com/android/server/IdleMaintenanceService.java b/services/core/java/com/android/server/IdleMaintenanceService.java
index b0a1aca..acc6abe 100644
--- a/services/core/java/com/android/server/IdleMaintenanceService.java
+++ b/services/core/java/com/android/server/IdleMaintenanceService.java
@@ -16,22 +16,38 @@
package com.android.server;
-import android.app.Activity;
-import android.app.ActivityManagerNative;
import android.app.AlarmManager;
import android.app.PendingIntent;
+import android.app.maintenance.IIdleCallback;
+import android.app.maintenance.IIdleService;
+import android.app.maintenance.IdleService;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.os.Handler;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.os.WorkSource;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseArray;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
/**
* This service observes the device state and when applicable sends
@@ -47,12 +63,15 @@ import android.util.Slog;
*
* The end of a maintenance window is announced only if: a start was
* announced AND the screen turned on or a dream was stopped.
+ *
+ * Method naming note:
+ * Methods whose name ends with "Tm" must only be called from the main thread.
*/
public class IdleMaintenanceService extends BroadcastReceiver {
private static final boolean DEBUG = false;
- private static final String LOG_TAG = IdleMaintenanceService.class.getSimpleName();
+ private static final String TAG = IdleMaintenanceService.class.getSimpleName();
private static final int LAST_USER_ACTIVITY_TIME_INVALID = -1;
@@ -74,36 +93,480 @@ public class IdleMaintenanceService extends BroadcastReceiver {
private static final String ACTION_FORCE_IDLE_MAINTENANCE =
"com.android.server.IdleMaintenanceService.action.FORCE_IDLE_MAINTENANCE";
- private static final Intent sIdleMaintenanceStartIntent;
- static {
- sIdleMaintenanceStartIntent = new Intent(Intent.ACTION_IDLE_MAINTENANCE_START);
- sIdleMaintenanceStartIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- };
+ static final int MSG_OP_COMPLETE = 1;
+ static final int MSG_IDLE_FINISHED = 2;
+ static final int MSG_TIMEOUT = 3;
- private static final Intent sIdleMaintenanceEndIntent;
- static {
- sIdleMaintenanceEndIntent = new Intent(Intent.ACTION_IDLE_MAINTENANCE_END);
- sIdleMaintenanceEndIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- }
+ // when a timeout happened, what were we expecting?
+ static final int VERB_BINDING = 1;
+ static final int VERB_IDLING = 2;
+ static final int VERB_ENDING = 3;
- private final AlarmManager mAlarmService;
+ // What are our relevant timeouts / allocated slices?
+ static final long OP_TIMEOUT = 8 * 1000; // 8 seconds to bind or ack the start
+ static final long IDLE_TIMESLICE = 10 * 60 * 1000; // ten minutes for each idler
+ private final AlarmManager mAlarmService;
private final BatteryService mBatteryService;
-
private final PendingIntent mUpdateIdleMaintenanceStatePendingIntent;
-
private final Context mContext;
-
private final WakeLock mWakeLock;
-
- private final Handler mHandler;
+ private final WorkSource mSystemWorkSource = new WorkSource(Process.myUid());
private long mLastIdleMaintenanceStartTimeMillis;
-
private long mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
-
private boolean mIdleMaintenanceStarted;
+ final IdleCallback mCallback;
+ final Handler mHandler;
+
+ final Random mTokenGenerator = new Random();
+
+ int makeToken() {
+ int token;
+ do {
+ token = mTokenGenerator.nextInt(Integer.MAX_VALUE);
+ } while (token == 0);
+ return token;
+ }
+
+ class ActiveTask {
+ public IdleServiceInfo who;
+ public int verb;
+ public int token;
+
+ ActiveTask(IdleServiceInfo target, int action) {
+ who = target;
+ verb = action;
+ token = makeToken();
+ }
+
+ @Override
+ public String toString() {
+ return "ActiveTask{" + Integer.toHexString(this.hashCode())
+ + " : verb=" + verb
+ + " : token=" + token
+ + " : "+ who + "}";
+ }
+ }
+
+ // What operations are in flight?
+ final SparseArray<ActiveTask> mPendingOperations = new SparseArray<ActiveTask>();
+
+ // Idle service queue management
+ class IdleServiceInfo {
+ public final ComponentName componentName;
+ public final int uid;
+ public IIdleService service;
+
+ IdleServiceInfo(ResolveInfo info, ComponentName cname) {
+ componentName = cname; // derived from 'info' but this avoids an extra object
+ uid = info.serviceInfo.applicationInfo.uid;
+ service = null;
+ }
+
+ @Override
+ public int hashCode() {
+ return componentName.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "IdleServiceInfo{" + componentName
+ + " / " + (service == null ? "null" : service.asBinder()) + "}";
+ }
+ }
+
+ final ArrayMap<ComponentName, IdleServiceInfo> mIdleServices =
+ new ArrayMap<ComponentName, IdleServiceInfo>();
+ final LinkedList<IdleServiceInfo> mIdleServiceQueue = new LinkedList<IdleServiceInfo>();
+ IdleServiceInfo mCurrentIdler; // set when we've committed to launching an idler
+ IdleServiceInfo mLastIdler; // end of queue when idling begins
+
+ void reportNoTimeout(int token, boolean result) {
+ final Message msg = mHandler.obtainMessage(MSG_OP_COMPLETE, result ? 1 : 0, token);
+ mHandler.sendMessage(msg);
+ }
+
+ // Binder acknowledgment trampoline
+ class IdleCallback extends IIdleCallback.Stub {
+ @Override
+ public void acknowledgeStart(int token, boolean result) throws RemoteException {
+ reportNoTimeout(token, result);
+ }
+
+ @Override
+ public void acknowledgeStop(int token) throws RemoteException {
+ reportNoTimeout(token, false);
+ }
+
+ @Override
+ public void idleFinished(int token) throws RemoteException {
+ if (DEBUG) {
+ Slog.v(TAG, "idleFinished: " + token);
+ }
+ final Message msg = mHandler.obtainMessage(MSG_IDLE_FINISHED, 0, token);
+ mHandler.sendMessage(msg);
+ }
+ }
+
+ // Stuff that we run on a Handler
+ class IdleHandler extends Handler {
+ public IdleHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ final int token = msg.arg2;
+
+ switch (msg.what) {
+ case MSG_OP_COMPLETE: {
+ if (DEBUG) {
+ Slog.i(TAG, "MSG_OP_COMPLETE of " + token);
+ }
+ ActiveTask task = mPendingOperations.get(token);
+ if (task != null) {
+ mPendingOperations.remove(token);
+ removeMessages(MSG_TIMEOUT);
+
+ handleOpCompleteTm(task, msg.arg1);
+ } else {
+ // Can happen in a race between timeout and actual
+ // (belated) completion of a "begin idling" or similar
+ // operation. In that state we've already processed the
+ // timeout, so we intentionally no-op here.
+ if (DEBUG) {
+ Slog.w(TAG, "Belated op-complete of " + token);
+ }
+ }
+ break;
+ }
+
+ case MSG_IDLE_FINISHED: {
+ if (DEBUG) {
+ Slog.i(TAG, "MSG_IDLE_FINISHED of " + token);
+ }
+ ActiveTask task = mPendingOperations.get(token);
+ if (task != null) {
+ if (DEBUG) {
+ Slog.i(TAG, "... removing task " + token);
+ }
+ mPendingOperations.remove(token);
+ removeMessages(MSG_TIMEOUT);
+
+ handleIdleFinishedTm(task);
+ } else {
+ // Can happen "legitimately" from an app explicitly calling
+ // idleFinished() after already having been told that its slice
+ // has ended.
+ if (DEBUG) {
+ Slog.w(TAG, "Belated idle-finished of " + token);
+ }
+ }
+ break;
+ }
+
+ case MSG_TIMEOUT: {
+ if (DEBUG) {
+ Slog.i(TAG, "MSG_TIMEOUT of " + token);
+ }
+ ActiveTask task = mPendingOperations.get(token);
+ if (task != null) {
+ mPendingOperations.remove(token);
+ removeMessages(MSG_OP_COMPLETE);
+
+ handleTimeoutTm(task);
+ } else {
+ // This one should not happen; we flushed timeout messages
+ // whenever we entered a state after which we have established
+ // that they are not appropriate.
+ Slog.w(TAG, "Unexpected timeout of " + token);
+ }
+ break;
+ }
+
+ default:
+ Slog.w(TAG, "Unknown message: " + msg.what);
+ }
+ }
+ }
+
+ void handleTimeoutTm(ActiveTask task) {
+ switch (task.verb) {
+ case VERB_BINDING: {
+ // We were trying to bind to this service, but it wedged or otherwise
+ // failed to respond in time. Let it stay in the queue for the next
+ // time around, but just give up on it for now and go on to the next.
+ startNextIdleServiceTm();
+ break;
+ }
+ case VERB_IDLING: {
+ // The service has reached the end of its designated idle timeslice.
+ // This is not considered an error.
+ if (DEBUG) {
+ Slog.i(TAG, "Idler reached end of timeslice: " + task.who);
+ }
+ sendEndIdleTm(task.who);
+ break;
+ }
+ case VERB_ENDING: {
+ if (mCurrentIdler == task.who) {
+ if (DEBUG) {
+ Slog.i(TAG, "Task timed out when ending; unbind needed");
+ }
+ handleIdleFinishedTm(task);
+ } else {
+ if (DEBUG) {
+ Slog.w(TAG, "Ending timeout for non-current idle service!");
+ }
+ }
+ break;
+ }
+ default: {
+ Slog.w(TAG, "Unknown timeout state " + task.verb);
+ break;
+ }
+ }
+ }
+
+ void handleOpCompleteTm(ActiveTask task, int result) {
+ if (DEBUG) {
+ Slog.i(TAG, "handleOpComplete : task=" + task + " result=" + result);
+ }
+ if (task.verb == VERB_IDLING) {
+ // If the service was told to begin idling and responded positively, then
+ // it has begun idling and will eventually either explicitly finish, or
+ // reach the end of its allotted timeslice. It's running free now, so we
+ // just schedule the idle-expiration timeout under the token it's already been
+ // given and let it keep going.
+ if (result != 0) {
+ scheduleOpTimeoutTm(task);
+ } else {
+ // The idle service has indicated that it does not, in fact,
+ // need to run at present, so we immediately indicate that it's
+ // to finish idling, and go on to the next idler.
+ if (DEBUG) {
+ Slog.i(TAG, "Idler declined idling; moving along");
+ }
+ sendEndIdleTm(task.who);
+ }
+ } else {
+ // In the idling case, the task will be cleared either as the result of a timeout
+ // or of an explicit idleFinished(). For all other operations (binding, ending) we
+ // are done with the task as such, so we remove it from our bookkeeping.
+ if (DEBUG) {
+ Slog.i(TAG, "Clearing task " + task);
+ }
+ mPendingOperations.remove(task.token);
+ if (task.verb == VERB_ENDING) {
+ // The last bit of handshaking around idle cessation for this target
+ handleIdleFinishedTm(task);
+ }
+ }
+ }
+
+ void handleIdleFinishedTm(ActiveTask task) {
+ final IdleServiceInfo who = task.who;
+ if (who == mCurrentIdler) {
+ if (DEBUG) {
+ Slog.i(TAG, "Current idler has finished: " + who);
+ Slog.i(TAG, "Attributing wakelock to system work source");
+ }
+ mContext.unbindService(mConnection);
+ startNextIdleServiceTm();
+ } else {
+ Slog.w(TAG, "finish from non-current idle service? " + who);
+ }
+ }
+
+ void updateIdleServiceQueueTm() {
+ if (DEBUG) {
+ Slog.i(TAG, "Updating idle service queue");
+ }
+ PackageManager pm = mContext.getPackageManager();
+ Intent idleIntent = new Intent(IdleService.SERVICE_INTERFACE);
+ List<ResolveInfo> services = pm.queryIntentServices(idleIntent, 0);
+ for (ResolveInfo info : services) {
+ if (info.serviceInfo != null) {
+ if (IdleService.PERMISSION_BIND.equals(info.serviceInfo.permission)) {
+ final ComponentName componentName = new ComponentName(
+ info.serviceInfo.packageName,
+ info.serviceInfo.name);
+ if (DEBUG) {
+ Slog.i(TAG, " - " + componentName);
+ }
+ if (!mIdleServices.containsKey(componentName)) {
+ if (DEBUG) {
+ Slog.i(TAG, " + not known; adding");
+ }
+ IdleServiceInfo serviceInfo = new IdleServiceInfo(info, componentName);
+ mIdleServices.put(componentName, serviceInfo);
+ mIdleServiceQueue.add(serviceInfo);
+ }
+ } else {
+ if (DEBUG) {
+ Slog.i(TAG, "Idle service " + info.serviceInfo
+ + " does not have required permission; ignoring");
+ }
+ }
+ }
+ }
+ }
+
+ void startNextIdleServiceTm() {
+ mWakeLock.setWorkSource(mSystemWorkSource);
+
+ if (mLastIdler == null) {
+ // we've run the queue; nothing more to do until the next idle interval.
+ if (DEBUG) {
+ Slog.i(TAG, "Queue already drained; nothing more to do");
+ }
+ return;
+ }
+
+ if (DEBUG) {
+ Slog.i(TAG, "startNextIdleService : last=" + mLastIdler + " cur=" + mCurrentIdler);
+ if (mIdleServiceQueue.size() > 0) {
+ int i = 0;
+ Slog.i(TAG, "Queue (" + mIdleServiceQueue.size() + "):");
+ for (IdleServiceInfo info : mIdleServiceQueue) {
+ Slog.i(TAG, " " + i + " : " + info);
+ i++;
+ }
+ }
+ }
+ if (mCurrentIdler != mLastIdler) {
+ if (mIdleServiceQueue.size() > 0) {
+ IdleServiceInfo target = mIdleServiceQueue.pop();
+ if (DEBUG) {
+ Slog.i(TAG, "starting next idle service " + target);
+ }
+ Intent idleIntent = new Intent(IdleService.SERVICE_INTERFACE);
+ idleIntent.setComponent(target.componentName);
+ mCurrentIdler = target;
+ ActiveTask task = new ActiveTask(target, VERB_BINDING);
+ scheduleOpTimeoutTm(task);
+ boolean bindOk = mContext.bindServiceAsUser(idleIntent, mConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY, UserHandle.OWNER);
+ if (!bindOk) {
+ if (DEBUG) {
+ Slog.w(TAG, "bindService() to " + target.componentName
+ + " failed");
+ }
+ } else {
+ mIdleServiceQueue.add(target); // at the end for next time
+ if (DEBUG) { Slog.i(TAG, "Attributing wakelock to target uid " + target.uid); }
+ mWakeLock.setWorkSource(new WorkSource(target.uid));
+ }
+ } else {
+ // Queue is empty but mLastIdler is non-null -- eeep. Clear *everything*
+ // and wind up until the next time around.
+ Slog.e(TAG, "Queue unexpectedly empty; resetting. last="
+ + mLastIdler + " cur=" + mCurrentIdler);
+ mHandler.removeMessages(MSG_TIMEOUT);
+ mPendingOperations.clear();
+ stopIdleMaintenanceTm();
+ }
+ } else {
+ // we've reached the place we started, so mark the queue as drained
+ if (DEBUG) {
+ Slog.i(TAG, "Reached end of queue.");
+ }
+ stopIdleMaintenanceTm();
+ }
+ }
+
+ void sendStartIdleTm(IdleServiceInfo who) {
+ ActiveTask task = new ActiveTask(who, VERB_IDLING);
+ scheduleOpTimeoutTm(task);
+ try {
+ who.service.startIdleMaintenance(mCallback, task.token);
+ } catch (RemoteException e) {
+ // We bound to it, but now we can't reach it. Bail and go on to the
+ // next service.
+ mContext.unbindService(mConnection);
+ if (DEBUG) { Slog.i(TAG, "Attributing wakelock to system work source"); }
+ mHandler.removeMessages(MSG_TIMEOUT);
+ startNextIdleServiceTm();
+ }
+ }
+
+ void sendEndIdleTm(IdleServiceInfo who) {
+ ActiveTask task = new ActiveTask(who, VERB_ENDING);
+ scheduleOpTimeoutTm(task);
+ if (DEBUG) {
+ Slog.i(TAG, "Sending end-idle to " + who);
+ }
+ try {
+ who.service.stopIdleMaintenance(mCallback, task.token);
+ } catch (RemoteException e) {
+ // We bound to it, but now we can't reach it. Bail and go on to the
+ // next service.
+ mContext.unbindService(mConnection);
+ if (DEBUG) { Slog.i(TAG, "Attributing wakelock to system work source"); }
+ mHandler.removeMessages(MSG_TIMEOUT);
+ startNextIdleServiceTm();
+ }
+ }
+
+ ServiceConnection mConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (DEBUG) {
+ Slog.i(TAG, "onServiceConnected(" + name + ")");
+ }
+ IdleServiceInfo info = mIdleServices.get(name);
+ if (info != null) {
+ // Bound! Cancel the bind timeout
+ mHandler.removeMessages(MSG_TIMEOUT);
+ // Now tell it to start its idle work
+ info.service = IIdleService.Stub.asInterface(service);
+ sendStartIdleTm(info);
+ } else {
+ // We bound to a service we don't know about. That's ungood.
+ Slog.e(TAG, "Connected to unexpected component " + name);
+ mContext.unbindService(this);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ if (DEBUG) {
+ Slog.i(TAG, "onServiceDisconnected(" + name + ")");
+ }
+ IdleServiceInfo who = mIdleServices.get(name);
+ if (who == mCurrentIdler) {
+ // Hm, okay; they didn't tell us they were finished but they
+ // went away. Crashed, probably. Oh well. They're gone, so
+ // we can't finish them cleanly; just force things along.
+ Slog.w(TAG, "Idler unexpectedly vanished: " + mCurrentIdler);
+ mContext.unbindService(this);
+ mHandler.removeMessages(MSG_TIMEOUT);
+ startNextIdleServiceTm();
+ } else {
+ // Not the current idler, so we don't interrupt our process...
+ if (DEBUG) {
+ Slog.w(TAG, "Disconnect of abandoned or unexpected service " + name);
+ }
+ }
+ }
+ };
+
+ // Schedules a timeout / end-of-work based on the task verb
+ void scheduleOpTimeoutTm(ActiveTask task) {
+ final long timeoutMillis = (task.verb == VERB_IDLING) ? IDLE_TIMESLICE : OP_TIMEOUT;
+ if (DEBUG) {
+ Slog.i(TAG, "Scheduling timeout (token " + task.token
+ + " : verb " + task.verb + ") for " + task + " in " + timeoutMillis);
+ }
+ mPendingOperations.put(task.token, task);
+ mHandler.removeMessages(MSG_TIMEOUT);
+ final Message msg = mHandler.obtainMessage(MSG_TIMEOUT, 0, task.token);
+ mHandler.sendMessageDelayed(msg, timeoutMillis);
+ }
+
+ // -------------------------------------------------------------------------------
public IdleMaintenanceService(Context context, BatteryService batteryService) {
mContext = context;
mBatteryService = batteryService;
@@ -111,9 +574,10 @@ public class IdleMaintenanceService extends BroadcastReceiver {
mAlarmService = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
+ mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
- mHandler = new Handler(mContext.getMainLooper());
+ mHandler = new IdleHandler(mContext.getMainLooper());
+ mCallback = new IdleCallback();
Intent intent = new Intent(ACTION_UPDATE_IDLE_MAINTENANCE_STATE);
intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
@@ -159,26 +623,28 @@ public class IdleMaintenanceService extends BroadcastReceiver {
mAlarmService.cancel(mUpdateIdleMaintenanceStatePendingIntent);
}
- private void updateIdleMaintenanceState(boolean noisy) {
+ private void updateIdleMaintenanceStateTm(boolean noisy) {
if (mIdleMaintenanceStarted) {
// Idle maintenance can be interrupted by user activity, or duration
// time out, or low battery.
- if (!lastUserActivityPermitsIdleMaintenanceRunning()
- || !batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning()) {
+ final boolean batteryOk
+ = batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning();
+ if (!lastUserActivityPermitsIdleMaintenanceRunning() || !batteryOk) {
unscheduleUpdateIdleMaintenanceState();
mIdleMaintenanceStarted = false;
- EventLogTags.writeIdleMaintenanceWindowFinish(SystemClock.elapsedRealtime(),
- mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
- isBatteryCharging() ? 1 : 0);
- sendIdleMaintenanceEndIntent();
// We stopped since we don't have enough battery or timed out but the
// user is not using the device, so we should be able to run maintenance
// in the next maintenance window since the battery may be charged
// without interaction and the min interval between maintenances passed.
- if (!batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning()) {
+ if (!batteryOk) {
scheduleUpdateIdleMaintenanceState(
getNextIdleMaintenanceIntervalStartFromNow());
}
+
+ EventLogTags.writeIdleMaintenanceWindowFinish(SystemClock.elapsedRealtime(),
+ mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
+ isBatteryCharging() ? 1 : 0);
+ scheduleIdleFinishTm();
}
} else if (deviceStatePermitsIdleMaintenanceStart(noisy)
&& lastUserActivityPermitsIdleMaintenanceStart(noisy)
@@ -191,7 +657,7 @@ public class IdleMaintenanceService extends BroadcastReceiver {
mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
isBatteryCharging() ? 1 : 0);
mLastIdleMaintenanceStartTimeMillis = SystemClock.elapsedRealtime();
- sendIdleMaintenanceStartIntent();
+ startIdleMaintenanceTm();
} else if (lastUserActivityPermitsIdleMaintenanceStart(noisy)) {
if (lastRunPermitsIdleMaintenanceStart(noisy)) {
// The user does not use the device and we did not run maintenance in more
@@ -207,25 +673,55 @@ public class IdleMaintenanceService extends BroadcastReceiver {
}
}
- private long getNextIdleMaintenanceIntervalStartFromNow() {
- return mLastIdleMaintenanceStartTimeMillis + MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS
- - SystemClock.elapsedRealtime();
+ void startIdleMaintenanceTm() {
+ if (DEBUG) {
+ Slog.i(TAG, "*** Starting idle maintenance ***");
+ }
+ if (DEBUG) { Slog.i(TAG, "Attributing wakelock to system work source"); }
+ mWakeLock.setWorkSource(mSystemWorkSource);
+ mWakeLock.acquire();
+ updateIdleServiceQueueTm();
+ mCurrentIdler = null;
+ mLastIdler = (mIdleServiceQueue.size() > 0) ? mIdleServiceQueue.peekLast() : null;
+ startNextIdleServiceTm();
}
- private void sendIdleMaintenanceStartIntent() {
- mWakeLock.acquire();
- try {
- ActivityManagerNative.getDefault().performIdleMaintenance();
- } catch (RemoteException e) {
+ // Start a graceful wind-down of the idle maintenance state: end the current idler
+ // and pretend that we've finished running the queue. If there's no current idler,
+ // this is a no-op.
+ void scheduleIdleFinishTm() {
+ if (mCurrentIdler != null) {
+ if (DEBUG) {
+ Slog.i(TAG, "*** Finishing idle maintenance ***");
+ }
+ mLastIdler = mCurrentIdler;
+ sendEndIdleTm(mCurrentIdler);
+ } else {
+ if (DEBUG) {
+ Slog.w(TAG, "Asked to finish idle maintenance but we're done already");
+ }
}
- mContext.sendOrderedBroadcastAsUser(sIdleMaintenanceStartIntent, UserHandle.ALL,
- null, this, mHandler, Activity.RESULT_OK, null, null);
}
- private void sendIdleMaintenanceEndIntent() {
- mWakeLock.acquire();
- mContext.sendOrderedBroadcastAsUser(sIdleMaintenanceEndIntent, UserHandle.ALL,
- null, this, mHandler, Activity.RESULT_OK, null, null);
+ // Actual finalization of the idle maintenance sequence
+ void stopIdleMaintenanceTm() {
+ if (mLastIdler != null) {
+ if (DEBUG) {
+ Slog.i(TAG, "*** Idle maintenance shutdown ***");
+ }
+ mWakeLock.setWorkSource(mSystemWorkSource);
+ mLastIdler = mCurrentIdler = null;
+ updateIdleMaintenanceStateTm(false); // resets 'started' and schedules next window
+ mWakeLock.release();
+ } else {
+ Slog.e(TAG, "ERROR: idle shutdown but invariants not held. last=" + mLastIdler
+ + " cur=" + mCurrentIdler + " size=" + mIdleServiceQueue.size());
+ }
+ }
+
+ private long getNextIdleMaintenanceIntervalStartFromNow() {
+ return mLastIdleMaintenanceStartTimeMillis + MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS
+ - SystemClock.elapsedRealtime();
}
private boolean deviceStatePermitsIdleMaintenanceStart(boolean noisy) {
@@ -281,7 +777,7 @@ public class IdleMaintenanceService extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) {
- Log.i(LOG_TAG, intent.getAction());
+ Log.i(TAG, intent.getAction());
}
String action = intent.getAction();
if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
@@ -292,7 +788,7 @@ public class IdleMaintenanceService extends BroadcastReceiver {
// next release. The only client for this for now is internal an holds
// a wake lock correctly.
if (mIdleMaintenanceStarted) {
- updateIdleMaintenanceState(false);
+ updateIdleMaintenanceStateTm(false);
}
} else if (Intent.ACTION_SCREEN_ON.equals(action)
|| Intent.ACTION_DREAMING_STOPPED.equals(action)) {
@@ -302,7 +798,7 @@ public class IdleMaintenanceService extends BroadcastReceiver {
unscheduleUpdateIdleMaintenanceState();
// If the screen went on/stopped dreaming, we know the user is using the
// device which means that idle maintenance should be stopped if running.
- updateIdleMaintenanceState(false);
+ updateIdleMaintenanceStateTm(false);
} else if (Intent.ACTION_SCREEN_OFF.equals(action)
|| Intent.ACTION_DREAMING_STARTED.equals(action)) {
mLastUserActivityElapsedTimeMillis = SystemClock.elapsedRealtime();
@@ -311,17 +807,12 @@ public class IdleMaintenanceService extends BroadcastReceiver {
// this timeout elapses since the device may go to sleep by then.
scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
} else if (ACTION_UPDATE_IDLE_MAINTENANCE_STATE.equals(action)) {
- updateIdleMaintenanceState(false);
+ updateIdleMaintenanceStateTm(false);
} else if (ACTION_FORCE_IDLE_MAINTENANCE.equals(action)) {
long now = SystemClock.elapsedRealtime() - 1;
mLastUserActivityElapsedTimeMillis = now - MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START;
mLastIdleMaintenanceStartTimeMillis = now - MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS;
- updateIdleMaintenanceState(true);
- } else if (Intent.ACTION_IDLE_MAINTENANCE_START.equals(action)
- || Intent.ACTION_IDLE_MAINTENANCE_END.equals(action)) {
- // We were holding a wake lock while broadcasting the idle maintenance
- // intents but now that we finished the broadcast release the wake lock.
- mWakeLock.release();
+ updateIdleMaintenanceStateTm(true);
}
}
}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 26424a5..0b3eb12 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -16,6 +16,8 @@
package com.android.server;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController;
+import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
import com.android.internal.inputmethod.InputMethodUtils;
import com.android.internal.inputmethod.InputMethodUtils.InputMethodSettings;
import com.android.internal.os.HandlerCaller;
@@ -114,12 +116,9 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Locale;
-import java.util.TreeMap;
/**
* This class provides a system service that manages input methods.
@@ -150,7 +149,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
- static final long TIME_TO_RECONNECT = 10*1000;
+ static final long TIME_TO_RECONNECT = 3 * 1000;
static final int SECURE_SUGGESTION_SPANS_MAX_SIZE = 20;
@@ -167,7 +166,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final HandlerCaller mCaller;
final boolean mHasFeature;
private InputMethodFileManager mFileManager;
- private InputMethodAndSubtypeListManager mImListManager;
private final HardKeyboardListener mHardKeyboardListener;
private final WindowManagerService mWindowManagerService;
@@ -179,6 +177,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final HashMap<String, InputMethodInfo> mMethodMap = new HashMap<String, InputMethodInfo>();
private final LruCache<SuggestionSpan, InputMethodInfo> mSecureSuggestionSpans =
new LruCache<SuggestionSpan, InputMethodInfo>(SECURE_SUGGESTION_SPANS_MAX_SIZE);
+ private final InputMethodSubtypeSwitchingController mSwitchingController;
// Used to bring IME service up to visible adjustment while it is being shown.
final ServiceConnection mVisibleConnection = new ServiceConnection() {
@@ -677,7 +676,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mSettings = new InputMethodSettings(
mRes, context.getContentResolver(), mMethodMap, mMethodList, userId);
mFileManager = new InputMethodFileManager(mMethodMap, userId);
- mImListManager = new InputMethodAndSubtypeListManager(context, this);
+ mSwitchingController = new InputMethodSubtypeSwitchingController(mSettings);
+ mSwitchingController.resetCircularListLocked(context);
// Just checking if defaultImiId is empty or not
final String defaultImiId = mSettings.getSelectedInputMethod();
@@ -757,8 +757,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (DEBUG) {
Slog.i(TAG, "Locale has been changed to " + newLocale);
}
- // InputMethodAndSubtypeListManager should be reset when the locale is changed.
- mImListManager = new InputMethodAndSubtypeListManager(mContext, this);
+ // CircularList should be reset when the locale is changed.
+ mSwitchingController.resetCircularListLocked(mContext);
buildInputMethodListLocked(mMethodList, mMethodMap, resetDefaultEnabledIme);
if (!updateOnlyWhenLocaleChanged) {
final String selectedImiId = mSettings.getSelectedInputMethod();
@@ -960,17 +960,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- private HashMap<InputMethodInfo, List<InputMethodSubtype>>
- getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked() {
- HashMap<InputMethodInfo, List<InputMethodSubtype>> enabledInputMethodAndSubtypes =
- new HashMap<InputMethodInfo, List<InputMethodSubtype>>();
- for (InputMethodInfo imi: mSettings.getEnabledInputMethodListLocked()) {
- enabledInputMethodAndSubtypes.put(
- imi, mSettings.getEnabledInputMethodSubtypeListLocked(mContext, imi, true));
- }
- return enabledInputMethodAndSubtypes;
- }
-
/**
* @param imiId if null, returns enabled subtypes for the current imi
* @return enabled subtypes of the specified imi
@@ -2153,7 +2142,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return false;
}
synchronized (mMethodMap) {
- final ImeSubtypeListItem nextSubtype = mImListManager.getNextInputMethod(
+ final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethod(
onlyCurrentIme, mMethodMap.get(mCurMethodId), mCurrentSubtype);
if (nextSubtype == null) {
return false;
@@ -2169,7 +2158,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return false;
}
synchronized (mMethodMap) {
- final ImeSubtypeListItem nextSubtype = mImListManager.getNextInputMethod(
+ final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethod(
false /* onlyCurrentIme */, mMethodMap.get(mCurMethodId), mCurrentSubtype);
if (nextSubtype == null) {
return false;
@@ -2242,6 +2231,22 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
return;
}
+ @Override
+ public int getInputMethodWindowVisibleHeight() {
+ return mWindowManagerService.getInputMethodWindowVisibleHeight();
+ }
+
+ @Override
+ public void notifyTextCommitted() {
+ if (DEBUG) {
+ Slog.d(TAG, "Got the notification of commitText");
+ }
+ final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
+ if (imi != null) {
+ mSwitchingController.onCommitText(imi, mCurrentSubtype);
+ }
+ }
+
private void setInputMethodWithSubtypeId(IBinder token, String id, int subtypeId) {
synchronized (mMethodMap) {
if (token == null) {
@@ -2620,7 +2625,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
synchronized (mMethodMap) {
final HashMap<InputMethodInfo, List<InputMethodSubtype>> immis =
- getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked();
+ mSettings.getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked(
+ mContext);
if (immis == null || immis.size() == 0) {
return;
}
@@ -2628,7 +2634,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
hideInputMethodMenuLocked();
final List<ImeSubtypeListItem> imList =
- mImListManager.getSortedInputMethodAndSubtypeList(
+ mSwitchingController.getSortedInputMethodAndSubtypeList(
showSubtypes, mInputShown, isScreenLocked);
if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) {
@@ -2747,66 +2753,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- private static class ImeSubtypeListItem implements Comparable<ImeSubtypeListItem> {
- public final CharSequence mImeName;
- public final CharSequence mSubtypeName;
- public final InputMethodInfo mImi;
- public final int mSubtypeId;
- private final boolean mIsSystemLocale;
- private final boolean mIsSystemLanguage;
-
- public ImeSubtypeListItem(CharSequence imeName, CharSequence subtypeName,
- InputMethodInfo imi, int subtypeId, String subtypeLocale, String systemLocale) {
- mImeName = imeName;
- mSubtypeName = subtypeName;
- mImi = imi;
- mSubtypeId = subtypeId;
- if (TextUtils.isEmpty(subtypeLocale)) {
- mIsSystemLocale = false;
- mIsSystemLanguage = false;
- } else {
- mIsSystemLocale = subtypeLocale.equals(systemLocale);
- mIsSystemLanguage = mIsSystemLocale
- || subtypeLocale.startsWith(systemLocale.substring(0, 2));
- }
- }
-
- @Override
- public int compareTo(ImeSubtypeListItem other) {
- if (TextUtils.isEmpty(mImeName)) {
- return 1;
- }
- if (TextUtils.isEmpty(other.mImeName)) {
- return -1;
- }
- if (!TextUtils.equals(mImeName, other.mImeName)) {
- return mImeName.toString().compareTo(other.mImeName.toString());
- }
- if (TextUtils.equals(mSubtypeName, other.mSubtypeName)) {
- return 0;
- }
- if (mIsSystemLocale) {
- return -1;
- }
- if (other.mIsSystemLocale) {
- return 1;
- }
- if (mIsSystemLanguage) {
- return -1;
- }
- if (other.mIsSystemLanguage) {
- return 1;
- }
- if (TextUtils.isEmpty(mSubtypeName)) {
- return 1;
- }
- if (TextUtils.isEmpty(other.mSubtypeName)) {
- return -1;
- }
- return mSubtypeName.toString().compareTo(other.mSubtypeName.toString());
- }
- }
-
private static class ImeSubtypeListAdapter extends ArrayAdapter<ImeSubtypeListItem> {
private final LayoutInflater mInflater;
private final int mTextViewResourceId;
@@ -3178,123 +3124,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- private static class InputMethodAndSubtypeListManager {
- private final Context mContext;
- // Used to load label
- private final PackageManager mPm;
- private final InputMethodManagerService mImms;
- private final String mSystemLocaleStr;
- public InputMethodAndSubtypeListManager(Context context, InputMethodManagerService imms) {
- mContext = context;
- mPm = context.getPackageManager();
- mImms = imms;
- final Locale locale = context.getResources().getConfiguration().locale;
- mSystemLocaleStr = locale != null ? locale.toString() : "";
- }
-
- private final TreeMap<InputMethodInfo, List<InputMethodSubtype>> mSortedImmis =
- new TreeMap<InputMethodInfo, List<InputMethodSubtype>>(
- new Comparator<InputMethodInfo>() {
- @Override
- public int compare(InputMethodInfo imi1, InputMethodInfo imi2) {
- if (imi2 == null) return 0;
- if (imi1 == null) return 1;
- if (mPm == null) {
- return imi1.getId().compareTo(imi2.getId());
- }
- CharSequence imiId1 = imi1.loadLabel(mPm) + "/" + imi1.getId();
- CharSequence imiId2 = imi2.loadLabel(mPm) + "/" + imi2.getId();
- return imiId1.toString().compareTo(imiId2.toString());
- }
- });
-
- public ImeSubtypeListItem getNextInputMethod(
- boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) {
- if (imi == null) {
- return null;
- }
- final List<ImeSubtypeListItem> imList = getSortedInputMethodAndSubtypeList();
- if (imList.size() <= 1) {
- return null;
- }
- final int N = imList.size();
- final int currentSubtypeId = subtype != null
- ? InputMethodUtils.getSubtypeIdFromHashCode(imi, subtype.hashCode())
- : NOT_A_SUBTYPE_ID;
- for (int i = 0; i < N; ++i) {
- final ImeSubtypeListItem isli = imList.get(i);
- if (isli.mImi.equals(imi) && isli.mSubtypeId == currentSubtypeId) {
- if (!onlyCurrentIme) {
- return imList.get((i + 1) % N);
- }
- for (int j = 0; j < N - 1; ++j) {
- final ImeSubtypeListItem candidate = imList.get((i + j + 1) % N);
- if (candidate.mImi.equals(imi)) {
- return candidate;
- }
- }
- return null;
- }
- }
- return null;
- }
-
- public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList() {
- return getSortedInputMethodAndSubtypeList(true, false, false);
- }
-
- public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList(boolean showSubtypes,
- boolean inputShown, boolean isScreenLocked) {
- final ArrayList<ImeSubtypeListItem> imList = new ArrayList<ImeSubtypeListItem>();
- final HashMap<InputMethodInfo, List<InputMethodSubtype>> immis =
- mImms.getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked();
- if (immis == null || immis.size() == 0) {
- return Collections.emptyList();
- }
- mSortedImmis.clear();
- mSortedImmis.putAll(immis);
- for (InputMethodInfo imi : mSortedImmis.keySet()) {
- if (imi == null) continue;
- List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList = immis.get(imi);
- HashSet<String> enabledSubtypeSet = new HashSet<String>();
- for (InputMethodSubtype subtype: explicitlyOrImplicitlyEnabledSubtypeList) {
- enabledSubtypeSet.add(String.valueOf(subtype.hashCode()));
- }
- final CharSequence imeLabel = imi.loadLabel(mPm);
- if (showSubtypes && enabledSubtypeSet.size() > 0) {
- final int subtypeCount = imi.getSubtypeCount();
- if (DEBUG) {
- Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId());
- }
- for (int j = 0; j < subtypeCount; ++j) {
- final InputMethodSubtype subtype = imi.getSubtypeAt(j);
- final String subtypeHashCode = String.valueOf(subtype.hashCode());
- // We show all enabled IMEs and subtypes when an IME is shown.
- if (enabledSubtypeSet.contains(subtypeHashCode)
- && ((inputShown && !isScreenLocked) || !subtype.isAuxiliary())) {
- final CharSequence subtypeLabel =
- subtype.overridesImplicitlyEnabledSubtype() ? null
- : subtype.getDisplayName(mContext, imi.getPackageName(),
- imi.getServiceInfo().applicationInfo);
- imList.add(new ImeSubtypeListItem(imeLabel, subtypeLabel, imi, j,
- subtype.getLocale(), mSystemLocaleStr));
-
- // Removing this subtype from enabledSubtypeSet because we no longer
- // need to add an entry of this subtype to imList to avoid duplicated
- // entries.
- enabledSubtypeSet.remove(subtypeHashCode);
- }
- }
- } else {
- imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_ID,
- null, mSystemLocaleStr));
- }
- }
- Collections.sort(imList);
- return imList;
- }
- }
-
// TODO: Cache the state for each user and reset when the cached user is removed.
private static class InputMethodFileManager {
private static final String SYSTEM_PATH = "system";
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index eebd1c5..473aa34 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -73,6 +73,9 @@ import com.android.server.location.LocationBlacklist;
import com.android.server.location.LocationFudger;
import com.android.server.location.LocationProviderInterface;
import com.android.server.location.LocationProviderProxy;
+import com.android.server.location.LocationRequestStatistics;
+import com.android.server.location.LocationRequestStatistics.PackageProviderKey;
+import com.android.server.location.LocationRequestStatistics.PackageStatistics;
import com.android.server.location.MockProvider;
import com.android.server.location.PassiveProvider;
@@ -178,6 +181,8 @@ public class LocationManagerService extends ILocationManager.Stub {
private final HashMap<String, ArrayList<UpdateRecord>> mRecordsByProvider =
new HashMap<String, ArrayList<UpdateRecord>>();
+ private final LocationRequestStatistics mRequestStatistics = new LocationRequestStatistics();
+
// mapping from provider name to last known location
private final HashMap<String, Location> mLastLocation = new HashMap<String, Location>();
@@ -568,7 +573,7 @@ public class LocationManagerService extends ILocationManager.Stub {
if (isAllowedByCurrentUserSettingsLocked(updateRecord.mProvider)) {
requestingLocation = true;
LocationProviderInterface locationProvider
- = mProvidersByName.get(updateRecord.mProvider);
+ = mProvidersByName.get(updateRecord.mProvider);
ProviderProperties properties = locationProvider != null
? locationProvider.getProperties() : null;
if (properties != null
@@ -813,7 +818,7 @@ public class LocationManagerService extends ILocationManager.Stub {
long identity = Binder.clearCallingIdentity();
receiver.decrementPendingBroadcastsLocked();
Binder.restoreCallingIdentity(identity);
- }
+ }
}
}
}
@@ -1288,13 +1293,18 @@ public class LocationManagerService extends ILocationManager.Stub {
if (!records.contains(this)) {
records.add(this);
}
+
+ // Update statistics for historical location requests by package/provider
+ mRequestStatistics.startRequesting(
+ mReceiver.mPackageName, provider, request.getInterval());
}
/**
- * Method to be called when a record will no longer be used. Calling this multiple times
- * must have the same effect as calling it once.
+ * Method to be called when a record will no longer be used.
*/
void disposeLocked(boolean removeReceiver) {
+ mRequestStatistics.stopRequesting(mReceiver.mPackageName, mProvider);
+
// remove from mRecordsByProvider
ArrayList<UpdateRecord> globalRecords = mRecordsByProvider.get(this.mProvider);
if (globalRecords != null) {
@@ -1541,6 +1551,7 @@ public class LocationManagerService extends ILocationManager.Stub {
if (oldRecords != null) {
// Call dispose() on the obsolete update records.
for (UpdateRecord record : oldRecords.values()) {
+ // Update statistics for historical location requests by package/provider
record.disposeLocked(false);
}
// Accumulate providers
@@ -1762,7 +1773,7 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public ProviderProperties getProviderProperties(String provider) {
if (mProvidersByName.get(provider) == null) {
- return null;
+ return null;
}
checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
@@ -1779,10 +1790,6 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public boolean isProviderEnabled(String provider) {
- // TODO: remove this check in next release, see b/10696351
- checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
- provider);
-
// Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
// so we discourage its use
if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
@@ -1965,7 +1972,7 @@ public class LocationManagerService extends ILocationManager.Stub {
// Fetch latest status update time
long newStatusUpdateTime = p.getStatusUpdateTime();
- // Get latest status
+ // Get latest status
Bundle extras = new Bundle();
int status = p.getStatus(extras);
@@ -2179,7 +2186,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
- PackageManager.PERMISSION_GRANTED) {
+ PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
}
}
@@ -2220,6 +2227,13 @@ public class LocationManagerService extends ILocationManager.Stub {
public void removeTestProvider(String provider) {
checkMockPermissionsSafe();
synchronized (mLock) {
+
+ // These methods can't be called after removing the test provider, so first make sure
+ // we don't leave anything dangling (cf b/11446702).
+ clearTestProviderEnabled(provider);
+ clearTestProviderLocation(provider);
+ clearTestProviderStatus(provider);
+
MockProvider mockProvider = mMockProviders.remove(provider);
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
@@ -2351,13 +2365,20 @@ public class LocationManagerService extends ILocationManager.Stub {
for (Receiver receiver : mReceivers.values()) {
pw.println(" " + receiver);
}
- pw.println(" Records by Provider:");
+ pw.println(" Active Records by Provider:");
for (Map.Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) {
pw.println(" " + entry.getKey() + ":");
for (UpdateRecord record : entry.getValue()) {
pw.println(" " + record);
}
}
+ pw.println(" Historical Records by Provider:");
+ for (Map.Entry<PackageProviderKey, PackageStatistics> entry
+ : mRequestStatistics.statistics.entrySet()) {
+ PackageProviderKey key = entry.getKey();
+ PackageStatistics stats = entry.getValue();
+ pw.println(" " + key.packageName + ": " + key.providerName + ": " + stats);
+ }
pw.println(" Last Known Locations:");
for (Map.Entry<String, Location> entry : mLastLocation.entrySet()) {
String provider = entry.getKey();
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 35e7afa..fe814fc 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -16,7 +16,6 @@
package com.android.server;
-import android.app.ActivityManagerNative;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
@@ -29,8 +28,6 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteStatement;
-import android.media.AudioManager;
-import android.media.AudioService;
import android.os.Binder;
import android.os.Environment;
import android.os.RemoteException;
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index f73a92b..a7ef424 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -73,13 +73,16 @@ import com.android.server.pm.UserManagerService;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
+import org.apache.commons.codec.binary.Hex;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
@@ -108,6 +111,9 @@ import javax.crypto.spec.PBEKeySpec;
class MountService extends IMountService.Stub
implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
+ // Static direct instance pointer for the tightly-coupled idle service to use
+ static MountService sSelf = null;
+
// TODO: listen for user creation/deletion
private static final boolean LOCAL_LOGD = false;
@@ -187,6 +193,12 @@ class MountService extends IMountService.Stub
public static final int FstrimCompleted = 700;
}
+ /** List of crypto types.
+ * These must match CRYPT_TYPE_XXX in cryptfs.h AND their
+ * corresponding commands in CommandListener.cpp */
+ public static final String[] CRYPTO_TYPES
+ = { "password", "default", "pattern", "pin" };
+
private Context mContext;
private NativeDaemonConnector mConnector;
@@ -346,6 +358,7 @@ class MountService extends IMountService.Stub
private static final int H_UNMOUNT_PM_DONE = 2;
private static final int H_UNMOUNT_MS = 3;
private static final int H_SYSTEM_READY = 4;
+ private static final int H_FSTRIM = 5;
private static final int RETRY_UNMOUNT_DELAY = 30; // in ms
private static final int MAX_UNMOUNT_RETRIES = 4;
@@ -514,6 +527,24 @@ class MountService extends IMountService.Stub
}
break;
}
+ case H_FSTRIM: {
+ waitForReady();
+ Slog.i(TAG, "Running fstrim idle maintenance");
+ try {
+ // This method must be run on the main (handler) thread,
+ // so it is safe to directly call into vold.
+ mConnector.execute("fstrim", "dotrim");
+ EventLogTags.writeFstrimStart(SystemClock.elapsedRealtime());
+ } catch (NativeDaemonConnectorException ndce) {
+ Slog.e(TAG, "Failed to run fstrim!");
+ }
+ // invoke the completion callback, if any
+ Runnable callback = (Runnable) msg.obj;
+ if (callback != null) {
+ callback.run();
+ }
+ break;
+ }
}
}
};
@@ -628,27 +659,6 @@ class MountService extends IMountService.Stub
}
};
- private final BroadcastReceiver mIdleMaintenanceReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- waitForReady();
- String action = intent.getAction();
- // Since fstrim will be run on a daily basis we do not expect
- // fstrim to be too long, so it is not interruptible. We will
- // implement interruption only in case we see issues.
- if (Intent.ACTION_IDLE_MAINTENANCE_START.equals(action)) {
- try {
- // This method runs on the handler thread,
- // so it is safe to directly call into vold.
- mConnector.execute("fstrim", "dotrim");
- EventLogTags.writeFstrimStart(SystemClock.elapsedRealtime());
- } catch (NativeDaemonConnectorException ndce) {
- Slog.e(TAG, "Failed to run fstrim!");
- }
- }
- }
- };
-
private final class MountServiceBinderListener implements IBinder.DeathRecipient {
final IMountServiceListener mListener;
@@ -666,6 +676,10 @@ class MountService extends IMountService.Stub
}
}
+ void runIdleMaintenance(Runnable callback) {
+ mHandler.sendMessage(mHandler.obtainMessage(H_FSTRIM, callback));
+ }
+
private void doShareUnshareVolume(String path, String method, boolean enable) {
// TODO: Add support for multiple share methods
if (!method.equals("ums")) {
@@ -803,6 +817,13 @@ class MountService extends IMountService.Stub
/**
* Callback from NativeDaemonConnector
*/
+ public boolean onCheckHoldWakeLock(int code) {
+ return false;
+ }
+
+ /**
+ * Callback from NativeDaemonConnector
+ */
public boolean onEvent(int code, String raw, String[] cooked) {
if (DEBUG_EVENTS) {
StringBuilder builder = new StringBuilder();
@@ -1357,6 +1378,8 @@ class MountService extends IMountService.Stub
* @param context Binder context for this service
*/
public MountService(Context context) {
+ sSelf = this;
+
mContext = context;
synchronized (mVolumesLock) {
@@ -1383,12 +1406,6 @@ class MountService extends IMountService.Stub
mUsbReceiver, new IntentFilter(UsbManager.ACTION_USB_STATE), null, mHandler);
}
- // Watch for idle maintenance changes
- IntentFilter idleMaintenanceFilter = new IntentFilter();
- idleMaintenanceFilter.addAction(Intent.ACTION_IDLE_MAINTENANCE_START);
- mContext.registerReceiverAsUser(mIdleMaintenanceReceiver, UserHandle.ALL,
- idleMaintenanceFilter, null, mHandler);
-
// Add OBB Action Handler to MountService thread.
mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
@@ -1397,7 +1414,8 @@ class MountService extends IMountService.Stub
* amount of containers we'd ever expect to have. This keeps an
* "asec list" from blocking a thread repeatedly.
*/
- mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25);
+ mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
+ null);
Thread thread = new Thread(mConnector, VOLD_TAG);
thread.start();
@@ -2056,6 +2074,14 @@ class MountService extends IMountService.Stub
}
}
+ private String toHex(String password) {
+ if (password == null) {
+ return null;
+ }
+ byte[] bytes = password.getBytes(StandardCharsets.UTF_8);
+ return new String(Hex.encodeHex(bytes));
+ }
+
@Override
public int decryptStorage(String password) {
if (TextUtils.isEmpty(password)) {
@@ -2073,7 +2099,7 @@ class MountService extends IMountService.Stub
final NativeDaemonEvent event;
try {
- event = mConnector.execute("cryptfs", "checkpw", new SensitiveArg(password));
+ event = mConnector.execute("cryptfs", "checkpw", new SensitiveArg(toHex(password)));
final int code = Integer.parseInt(event.getMessage());
if (code == 0) {
@@ -2112,7 +2138,8 @@ class MountService extends IMountService.Stub
}
try {
- mConnector.execute("cryptfs", "enablecrypto", "inplace", new SensitiveArg(password));
+ mConnector.execute("cryptfs", "enablecrypto", "inplace",
+ new SensitiveArg(toHex(password)));
} catch (NativeDaemonConnectorException e) {
// Encryption failed
return e.getCode();
@@ -2121,11 +2148,11 @@ class MountService extends IMountService.Stub
return 0;
}
- public int changeEncryptionPassword(String password) {
- if (TextUtils.isEmpty(password)) {
- throw new IllegalArgumentException("password cannot be empty");
- }
-
+ /** Set the password for encrypting the master key.
+ * @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager.
+ * @param password The password to set.
+ */
+ public int changeEncryptionPassword(int type, String password) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
"no permission to access the crypt keeper");
@@ -2137,7 +2164,8 @@ class MountService extends IMountService.Stub
final NativeDaemonEvent event;
try {
- event = mConnector.execute("cryptfs", "changepw", new SensitiveArg(password));
+ event = mConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type],
+ new SensitiveArg(toHex(password)));
return Integer.parseInt(event.getMessage());
} catch (NativeDaemonConnectorException e) {
// Encryption failed
@@ -2170,7 +2198,7 @@ class MountService extends IMountService.Stub
final NativeDaemonEvent event;
try {
- event = mConnector.execute("cryptfs", "verifypw", new SensitiveArg(password));
+ event = mConnector.execute("cryptfs", "verifypw", new SensitiveArg(toHex(password)));
Slog.i(TAG, "cryptfs verifypw => " + event.getMessage());
return Integer.parseInt(event.getMessage());
} catch (NativeDaemonConnectorException e) {
@@ -2179,6 +2207,29 @@ class MountService extends IMountService.Stub
}
}
+ /**
+ * Get the type of encryption used to encrypt the master key.
+ * @return The type, one of the CRYPT_TYPE_XXX consts from StorageManager.
+ */
+ @Override
+ public int getPasswordType() throws RemoteException {
+
+ waitForReady();
+
+ final NativeDaemonEvent event;
+ try {
+ event = mConnector.execute("cryptfs", "getpwtype");
+ for (int i = 0; i < CRYPTO_TYPES.length; ++i) {
+ if (CRYPTO_TYPES[i].equals(event.getMessage()))
+ return i;
+ }
+
+ throw new IllegalStateException("unexpected return from cryptfs");
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
@Override
public int mkdirs(String callingPkg, String appPath) {
final int userId = UserHandle.getUserId(Binder.getCallingUid());
diff --git a/services/core/java/com/android/server/MountServiceIdler.java b/services/core/java/com/android/server/MountServiceIdler.java
new file mode 100644
index 0000000..8b19321
--- /dev/null
+++ b/services/core/java/com/android/server/MountServiceIdler.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.maintenance.IdleService;
+import android.util.Slog;
+
+public class MountServiceIdler extends IdleService {
+ private static final String TAG = "MountServiceIdler";
+
+ private Runnable mFinishCallback = new Runnable() {
+ @Override
+ public void run() {
+ Slog.i(TAG, "Got mount service completion callback");
+ finishIdle();
+ }
+ };
+
+ @Override
+ public boolean onIdleStart() {
+ // The mount service will run an fstrim operation asynchronously
+ // on a designated separate thread, so we provide it with a callback
+ // that lets us cleanly end our idle timeslice. It's safe to call
+ // finishIdle() from any thread.
+ MountService ms = MountService.sSelf;
+ if (ms != null) {
+ ms.runIdleMaintenance(mFinishCallback);
+ }
+ return ms != null;
+ }
+
+ @Override
+ public void onIdleStop() {
+ }
+}
diff --git a/services/core/java/com/android/server/NativeDaemonConnector.java b/services/core/java/com/android/server/NativeDaemonConnector.java
index 417d6d8..265b957 100644
--- a/services/core/java/com/android/server/NativeDaemonConnector.java
+++ b/services/core/java/com/android/server/NativeDaemonConnector.java
@@ -21,6 +21,7 @@ import android.net.LocalSocketAddress;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
+import android.os.PowerManager;
import android.os.SystemClock;
import android.util.LocalLog;
import android.util.Slog;
@@ -56,6 +57,8 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
private final ResponseQueue mResponseQueue;
+ private final PowerManager.WakeLock mWakeLock;
+
private INativeDaemonConnectorCallbacks mCallbacks;
private Handler mCallbackHandler;
@@ -70,10 +73,14 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
private final int BUFFER_SIZE = 4096;
NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,
- int responseQueueSize, String logTag, int maxLogSize) {
+ int responseQueueSize, String logTag, int maxLogSize, PowerManager.WakeLock wl) {
mCallbacks = callbacks;
mSocket = socket;
mResponseQueue = new ResponseQueue(responseQueueSize);
+ mWakeLock = wl;
+ if (mWakeLock != null) {
+ mWakeLock.setReferenceCounted(true);
+ }
mSequenceNumber = new AtomicInteger(0);
TAG = logTag != null ? logTag : "NativeDaemonConnector";
mLocalLog = new LocalLog(maxLogSize);
@@ -102,6 +109,10 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
}
} catch (Exception e) {
loge("Error handling '" + event + "': " + e);
+ } finally {
+ if (mCallbacks.onCheckHoldWakeLock(msg.what)) {
+ mWakeLock.release();
+ }
}
return true;
}
@@ -154,18 +165,29 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
buffer, start, i - start, StandardCharsets.UTF_8);
log("RCV <- {" + rawEvent + "}");
+ boolean releaseWl = false;
try {
final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent(
rawEvent);
if (event.isClassUnsolicited()) {
// TODO: migrate to sending NativeDaemonEvent instances
- mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage(
- event.getCode(), event.getRawEvent()));
+ if (mCallbacks.onCheckHoldWakeLock(event.getCode())) {
+ mWakeLock.acquire();
+ releaseWl = true;
+ }
+ if (mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage(
+ event.getCode(), event.getRawEvent()))) {
+ releaseWl = false;
+ }
} else {
mResponseQueue.add(event.getCmdNumber(), event);
}
} catch (IllegalArgumentException e) {
log("Problem parsing message: " + rawEvent + " - " + e);
+ } finally {
+ if (releaseWl) {
+ mWakeLock.acquire();
+ }
}
start = i + 1;
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index e6c5422..bfc966b 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -20,6 +20,7 @@ import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.DUMP;
import static android.Manifest.permission.SHUTDOWN;
import static android.net.NetworkStats.SET_DEFAULT;
+import static android.net.NetworkStats.TAG_ALL;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.TrafficStats.UID_TETHERING;
@@ -36,6 +37,7 @@ import static com.android.server.NetworkManagementService.NetdResponseCode.TtyLi
import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
import android.content.Context;
+import android.net.ConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
@@ -47,7 +49,9 @@ import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Handler;
+import android.os.INetworkActivityListener;
import android.os.INetworkManagementService;
+import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -80,7 +84,6 @@ import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -152,6 +155,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub
private final Handler mMainHandler = new Handler();
+ private IBatteryStats mBatteryStats;
+
private Thread mThread;
private CountDownLatch mConnectedSignal = new CountDownLatch(1);
@@ -172,12 +177,12 @@ public class NetworkManagementService extends INetworkManagementService.Stub
/** Set of interfaces with active idle timers. */
private static class IdleTimerParams {
public final int timeout;
- public final String label;
+ public final int type;
public int networkCount;
- IdleTimerParams(int timeout, String label) {
+ IdleTimerParams(int timeout, int type) {
this.timeout = timeout;
- this.label = label;
+ this.type = type;
this.networkCount = 1;
}
}
@@ -186,6 +191,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub
private volatile boolean mBandwidthControlEnabled;
private volatile boolean mFirewallEnabled;
+ private final RemoteCallbackList<INetworkActivityListener> mNetworkActivityListeners =
+ new RemoteCallbackList<INetworkActivityListener>();
+ private boolean mNetworkActive;
+
/**
* Constructs a new NetworkManagementService instance
*
@@ -198,8 +207,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub
return;
}
+ PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, NETD_TAG);
+
mConnector = new NativeDaemonConnector(
- new NetdCallbackReceiver(), socket, 10, NETD_TAG, 160);
+ new NetdCallbackReceiver(), socket, 10, NETD_TAG, 160, wl);
mThread = new Thread(mConnector, NETD_TAG);
// Add ourself to the Watchdog monitors.
@@ -227,6 +239,17 @@ public class NetworkManagementService extends INetworkManagementService.Stub
if (DBG) Slog.d(TAG, "Prepared");
}
+ private IBatteryStats getBatteryStats() {
+ synchronized (this) {
+ if (mBatteryStats != null) {
+ return mBatteryStats;
+ }
+ mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
+ BatteryStats.SERVICE_NAME));
+ return mBatteryStats;
+ }
+ }
+
@Override
public void registerObserver(INetworkManagementEventObserver observer) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
@@ -323,16 +346,38 @@ public class NetworkManagementService extends INetworkManagementService.Stub
/**
* Notify our observers of a change in the data activity state of the interface
*/
- private void notifyInterfaceClassActivity(String label, boolean active) {
+ private void notifyInterfaceClassActivity(int type, boolean active) {
+ try {
+ getBatteryStats().noteDataConnectionActive(type, active);
+ } catch (RemoteException e) {
+ }
+
final int length = mObservers.beginBroadcast();
for (int i = 0; i < length; i++) {
try {
- mObservers.getBroadcastItem(i).interfaceClassDataActivityChanged(label, active);
+ mObservers.getBroadcastItem(i).interfaceClassDataActivityChanged(
+ Integer.toString(type), active);
} catch (RemoteException e) {
} catch (RuntimeException e) {
}
}
mObservers.finishBroadcast();
+
+ boolean report = false;
+ synchronized (mIdleTimerLock) {
+ if (mActiveIdleTimers.isEmpty()) {
+ // If there are no idle times, we are not monitoring activity, so we
+ // are always considered active.
+ active = true;
+ }
+ if (mNetworkActive != active) {
+ mNetworkActive = active;
+ report = active;
+ }
+ }
+ if (report) {
+ reportNetworkActive();
+ }
}
/**
@@ -360,8 +405,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
if (mBandwidthControlEnabled) {
try {
- IBatteryStats.Stub.asInterface(ServiceManager.getService(BatteryStats.SERVICE_NAME))
- .noteNetworkStatsEnabled();
+ getBatteryStats().noteNetworkStatsEnabled();
} catch (RemoteException e) {
}
}
@@ -471,6 +515,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
@Override
+ public boolean onCheckHoldWakeLock(int code) {
+ return code == NetdResponseCode.InterfaceClassActivity;
+ }
+
+ @Override
public boolean onEvent(int code, String raw, String[] cooked) {
String errorMessage = String.format("Invalid event from daemon (%s)", raw);
switch (code) {
@@ -523,7 +572,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
throw new IllegalStateException(errorMessage);
}
boolean isActive = cooked[2].equals("active");
- notifyInterfaceClassActivity(cooked[3], isActive);
+ notifyInterfaceClassActivity(Integer.parseInt(cooked[3]), isActive);
return true;
// break;
case NetdResponseCode.InterfaceAddressChange:
@@ -1185,7 +1234,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
@Override
- public void addIdleTimer(String iface, int timeout, String label) {
+ public void addIdleTimer(String iface, int timeout, final int type) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
if (DBG) Slog.d(TAG, "Adding idletimer");
@@ -1199,11 +1248,22 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
try {
- mConnector.execute("idletimer", "add", iface, Integer.toString(timeout), label);
+ mConnector.execute("idletimer", "add", iface, Integer.toString(timeout),
+ Integer.toString(type));
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
- mActiveIdleTimers.put(iface, new IdleTimerParams(timeout, label));
+ mActiveIdleTimers.put(iface, new IdleTimerParams(timeout, type));
+
+ // Networks start up.
+ if (ConnectivityManager.isNetworkTypeMobile(type)) {
+ mNetworkActive = false;
+ }
+ mMainHandler.post(new Runnable() {
+ @Override public void run() {
+ notifyInterfaceClassActivity(type, true);
+ }
+ });
}
}
@@ -1214,18 +1274,23 @@ public class NetworkManagementService extends INetworkManagementService.Stub
if (DBG) Slog.d(TAG, "Removing idletimer");
synchronized (mIdleTimerLock) {
- IdleTimerParams params = mActiveIdleTimers.get(iface);
+ final IdleTimerParams params = mActiveIdleTimers.get(iface);
if (params == null || --(params.networkCount) > 0) {
return;
}
try {
mConnector.execute("idletimer", "remove", iface,
- Integer.toString(params.timeout), params.label);
+ Integer.toString(params.timeout), Integer.toString(params.type));
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
mActiveIdleTimers.remove(iface);
+ mMainHandler.post(new Runnable() {
+ @Override public void run() {
+ notifyInterfaceClassActivity(params.type, false);
+ }
+ });
}
}
@@ -1253,7 +1318,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
public NetworkStats getNetworkStatsDetail() {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- return mStatsFactory.readNetworkStatsDetail(UID_ALL);
+ return mStatsFactory.readNetworkStatsDetail(UID_ALL, null, TAG_ALL, null);
} catch (IOException e) {
throw new IllegalStateException(e);
}
@@ -1414,7 +1479,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
public NetworkStats getNetworkStatsUidDetail(int uid) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- return mStatsFactory.readNetworkStatsDetail(uid);
+ return mStatsFactory.readNetworkStatsDetail(uid, null, TAG_ALL, null);
} catch (IOException e) {
throw new IllegalStateException(e);
}
@@ -1784,6 +1849,35 @@ public class NetworkManagementService extends INetworkManagementService.Stub
return event.getMessage().endsWith("started");
}
+ @Override
+ public void registerNetworkActivityListener(INetworkActivityListener listener) {
+ mNetworkActivityListeners.register(listener);
+ }
+
+ @Override
+ public void unregisterNetworkActivityListener(INetworkActivityListener listener) {
+ mNetworkActivityListeners.unregister(listener);
+ }
+
+ @Override
+ public boolean isNetworkActive() {
+ synchronized (mNetworkActivityListeners) {
+ return mNetworkActive || mActiveIdleTimers.isEmpty();
+ }
+ }
+
+ private void reportNetworkActive() {
+ final int length = mNetworkActivityListeners.beginBroadcast();
+ for (int i = 0; i < length; i++) {
+ try {
+ mNetworkActivityListeners.getBroadcastItem(i).onNetworkActive();
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
+ }
+ mNetworkActivityListeners.finishBroadcast();
+ }
+
/** {@inheritDoc} */
@Override
public void monitor() {
@@ -1817,6 +1911,17 @@ public class NetworkManagementService extends INetworkManagementService.Stub
pw.println("]");
}
+ synchronized (mIdleTimerLock) {
+ pw.println("Idle timers:");
+ for (HashMap.Entry<String, IdleTimerParams> ent : mActiveIdleTimers.entrySet()) {
+ pw.print(" "); pw.print(ent.getKey()); pw.println(":");
+ IdleTimerParams params = ent.getValue();
+ pw.print(" timeout="); pw.print(params.timeout);
+ pw.print(" type="); pw.print(params.type);
+ pw.print(" networkCount="); pw.println(params.networkCount);
+ }
+ }
+
pw.print("Firewall enabled: "); pw.println(mFirewallEnabled);
}
}
diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java
index 16d2468..c9f9a25 100644
--- a/services/core/java/com/android/server/NsdService.java
+++ b/services/core/java/com/android/server/NsdService.java
@@ -26,11 +26,8 @@ import android.net.nsd.DnsSdTxtRecord;
import android.net.nsd.INsdManager;
import android.net.nsd.NsdManager;
import android.os.Binder;
-import android.os.Handler;
-import android.os.HandlerThread;
import android.os.Message;
import android.os.Messenger;
-import android.os.IBinder;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
@@ -39,20 +36,13 @@ import android.util.SparseArray;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.net.InetAddress;
-import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
import java.util.concurrent.CountDownLatch;
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
-import com.android.server.am.BatteryStatsService;
-import com.android.server.NativeDaemonConnector.Command;
-import com.android.internal.R;
/**
* Network Service Discovery Service handles remote service discovery operation requests by
@@ -544,7 +534,7 @@ public class NsdService extends INsdManager.Stub {
mContentResolver = context.getContentResolver();
mNativeConnector = new NativeDaemonConnector(new NativeCallbackReceiver(), "mdns", 10,
- MDNS_TAG, 25);
+ MDNS_TAG, 25, null);
mNsdStateMachine = new NsdStateMachine(TAG);
mNsdStateMachine.start();
@@ -632,6 +622,10 @@ public class NsdService extends INsdManager.Stub {
mNativeDaemonConnected.countDown();
}
+ public boolean onCheckHoldWakeLock(int code) {
+ return false;
+ }
+
public boolean onEvent(int code, String raw, String[] cooked) {
// TODO: NDC translates a message to a callback, we could enhance NDC to
// directly interact with a state machine through messages
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index 5c7bfab..9274295 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -37,7 +37,6 @@ import com.android.internal.content.PackageMonitor;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
diff --git a/services/core/java/com/android/server/ShutdownActivity.java b/services/core/java/com/android/server/ShutdownActivity.java
index be65141..56172ed 100644
--- a/services/core/java/com/android/server/ShutdownActivity.java
+++ b/services/core/java/com/android/server/ShutdownActivity.java
@@ -20,14 +20,11 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
-import android.os.Handler;
import android.os.IPowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Slog;
-import com.android.server.power.ShutdownThread;
-
public class ShutdownActivity extends Activity {
private static final String TAG = "ShutdownActivity";
diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java
index 0964767..d4c436f 100644
--- a/services/core/java/com/android/server/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/TextServicesManagerService.java
@@ -33,7 +33,6 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
diff --git a/services/core/java/com/android/server/UpdateLockService.java b/services/core/java/com/android/server/UpdateLockService.java
index 0f778cd..7f33973 100644
--- a/services/core/java/com/android/server/UpdateLockService.java
+++ b/services/core/java/com/android/server/UpdateLockService.java
@@ -24,7 +24,6 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.IUpdateLock;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.TokenWatcher;
import android.os.UpdateLock;
import android.os.UserHandle;
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index aa9849e..f587ccc 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -863,6 +863,7 @@ public class AccountManagerService
"User cannot modify accounts");
} catch (RemoteException re) {
}
+ return;
}
long identityToken = clearCallingIdentity();
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index d66c5a7..a845127 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -28,6 +28,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.SystemProperties;
import android.util.ArrayMap;
+import com.android.internal.app.ProcessMap;
import com.android.internal.app.ProcessStats;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.TransferPipe;
@@ -596,12 +597,7 @@ public final class ActiveServices {
break;
}
}
- if (anyForeground != proc.foregroundServices) {
- proc.foregroundServices = anyForeground;
- if (oomAdj) {
- mAm.updateOomAdjLocked();
- }
- }
+ mAm.updateProcessForegroundLocked(proc, anyForeground, oomAdj);
}
private boolean updateServiceClientActivitiesLocked(ProcessRecord proc,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4dde117..f8d7821 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -32,6 +32,7 @@ import android.app.IActivityContainer;
import android.app.IActivityContainerCallback;
import android.appwidget.AppWidgetManager;
import android.graphics.Rect;
+import android.os.BatteryStats;
import android.util.ArrayMap;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -920,11 +921,25 @@ public final class ActivityManagerService extends ActivityManagerNative
long mLowRamTimeSinceLastIdle = 0;
/**
- * If RAM is currently low, when that horrible situatin started.
+ * If RAM is currently low, when that horrible situation started.
*/
long mLowRamStartTime = 0;
/**
+ * For reporting to battery stats the current top application.
+ */
+ private String mCurResumedPackage = null;
+ private int mCurResumedUid = -1;
+
+ /**
+ * For reporting to battery stats the apps currently running foreground
+ * service. The ProcessMap is package/uid tuples; each of these contain
+ * an array of the currently foreground processes.
+ */
+ final ProcessMap<ArrayList<ProcessRecord>> mForegroundPackages
+ = new ProcessMap<ArrayList<ProcessRecord>>();
+
+ /**
* This is set if we had to do a delayed dexopt of an app before launching
* it, to increasing the ANR timeouts in that case.
*/
@@ -1331,7 +1346,7 @@ public final class ActivityManagerService extends ActivityManagerNative
String pkg = bundle.getString("pkg");
String reason = bundle.getString("reason");
forceStopPackageLocked(pkg, appid, restart, false, true, false,
- UserHandle.USER_ALL, reason);
+ false, UserHandle.USER_ALL, reason);
}
} break;
case FINALIZE_PENDING_INTENT_MSG: {
@@ -2100,29 +2115,24 @@ public final class ActivityManagerService extends ActivityManagerNative
totalUTime += otherUTime;
totalSTime += otherSTime;
if (pr != null) {
- BatteryStatsImpl.Uid.Proc ps = bstats.getProcessStatsLocked(
- st.name, st.pid);
+ BatteryStatsImpl.Uid.Proc ps = pr.curProcBatteryStats;
+ if (ps == null || !ps.isActive()) {
+ pr.curProcBatteryStats = ps = bstats.getProcessStatsLocked(
+ pr.info.uid, pr.processName);
+ }
ps.addCpuTimeLocked(st.rel_utime-otherUTime,
st.rel_stime-otherSTime);
ps.addSpeedStepTimes(cpuSpeedTimes);
pr.curCpuTime += (st.rel_utime+st.rel_stime) * 10;
- } else if (st.uid >= Process.FIRST_APPLICATION_UID) {
+ } else {
BatteryStatsImpl.Uid.Proc ps = st.batteryStats;
- if (ps == null) {
- st.batteryStats = ps = bstats.getProcessStatsLocked(st.uid,
- "(Unknown)");
+ if (ps == null || !ps.isActive()) {
+ st.batteryStats = ps = bstats.getProcessStatsLocked(
+ bstats.mapUid(st.uid), st.name);
}
ps.addCpuTimeLocked(st.rel_utime-otherUTime,
st.rel_stime-otherSTime);
ps.addSpeedStepTimes(cpuSpeedTimes);
- } else {
- BatteryStatsImpl.Uid.Proc ps =
- bstats.getProcessStatsLocked(st.name, st.pid);
- if (ps != null) {
- ps.addCpuTimeLocked(st.rel_utime-otherUTime,
- st.rel_stime-otherSTime);
- ps.addSpeedStepTimes(cpuSpeedTimes);
- }
}
}
bstats.finishAddingCpuLocked(perc, totalUTime,
@@ -2771,6 +2781,11 @@ public final class ActivityManagerService extends ActivityManagerNative
mHandler.sendMessageDelayed(msg, startResult.usingWrapper
? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
}
+ mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_PROC_START,
+ app.processName, app.info.uid);
+ if (app.isolated) {
+ mBatteryStatsService.addIsolatedUid(app.uid, app.info.uid);
+ }
} catch (RuntimeException e) {
// XXX do better error recovery.
app.setPid(0);
@@ -4443,7 +4458,7 @@ public final class ActivityManagerService extends ActivityManagerNative
private void forceStopPackageLocked(final String packageName, int uid, String reason) {
forceStopPackageLocked(packageName, UserHandle.getAppId(uid), false,
- false, true, false, UserHandle.getUserId(uid), reason);
+ false, true, false, false, UserHandle.getUserId(uid), reason);
Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
Uri.fromParts("package", packageName, null));
if (!mProcessesReady) {
@@ -4459,7 +4474,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
private void forceStopUserLocked(int userId, String reason) {
- forceStopPackageLocked(null, -1, false, false, true, false, userId, reason);
+ forceStopPackageLocked(null, -1, false, false, true, false, false, userId, reason);
Intent intent = new Intent(Intent.ACTION_USER_STOPPED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
@@ -4544,7 +4559,7 @@ public final class ActivityManagerService extends ActivityManagerNative
private final boolean forceStopPackageLocked(String name, int appId,
boolean callerWillRestart, boolean purgeCache, boolean doit,
- boolean evenPersistent, int userId, String reason) {
+ boolean evenPersistent, boolean uninstalling, int userId, String reason) {
int i;
int N;
@@ -4636,7 +4651,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// Remove transient permissions granted from/to this package/user
removeUriPermissionsForPackageLocked(name, userId, false);
- if (name == null) {
+ if (name == null || uninstalling) {
// Remove pending intents. For now we only do this when force
// stopping users, because we have some problems when doing this
// for packages -- app widgets are not currently cleaned up for
@@ -4726,6 +4741,11 @@ public final class ActivityManagerService extends ActivityManagerNative
mPidsSelfLocked.remove(pid);
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
}
+ mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_PROC_FINISH,
+ app.processName, app.info.uid);
+ if (app.isolated) {
+ mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
+ }
killUnneededProcessLocked(app, reason);
handleAppDiedLocked(app, true, allowRestart);
removeLruProcessLocked(app);
@@ -4766,6 +4786,11 @@ public final class ActivityManagerService extends ActivityManagerNative
mHeavyWeightProcess.userId, 0));
mHeavyWeightProcess = null;
}
+ mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_PROC_FINISH,
+ app.processName, app.info.uid);
+ if (app.isolated) {
+ mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
+ }
// Take care of any launching providers waiting for this process.
checkAppInLaunchingProvidersLocked(app, true);
// Take care of any services that are waiting for the process.
@@ -4850,7 +4875,7 @@ public final class ActivityManagerService extends ActivityManagerNative
app.curAdj = app.setAdj = -100;
app.curSchedGroup = app.setSchedGroup = Process.THREAD_GROUP_DEFAULT;
app.forcingToForeground = null;
- app.foregroundServices = false;
+ updateProcessForegroundLocked(app, false, false);
app.hasShownUi = false;
app.debugging = false;
app.cached = false;
@@ -5081,7 +5106,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (pkgs != null) {
for (String pkg : pkgs) {
synchronized (ActivityManagerService.this) {
- if (forceStopPackageLocked(pkg, -1, false, false, false, false, 0,
+ if (forceStopPackageLocked(pkg, -1, false, false, false, false, false, 0,
"finished booting")) {
setResultCode(Activity.RESULT_OK);
return;
@@ -5522,6 +5547,38 @@ public final class ActivityManagerService extends ActivityManagerNative
}
@Override
+ public String getTagForIntentSender(IIntentSender pendingResult, String prefix) {
+ if (!(pendingResult instanceof PendingIntentRecord)) {
+ return null;
+ }
+ try {
+ PendingIntentRecord res = (PendingIntentRecord)pendingResult;
+ Intent intent = res.key.requestIntent;
+ if (intent != null) {
+ if (res.lastTag != null && res.lastTagPrefix == prefix && (res.lastTagPrefix == null
+ || res.lastTagPrefix.equals(prefix))) {
+ return res.lastTag;
+ }
+ res.lastTagPrefix = prefix;
+ StringBuilder sb = new StringBuilder(128);
+ if (prefix != null) {
+ sb.append(prefix);
+ }
+ if (intent.getAction() != null) {
+ sb.append(intent.getAction());
+ } else if (intent.getComponent() != null) {
+ intent.getComponent().appendShortString(sb);
+ } else {
+ sb.append("?");
+ }
+ return res.lastTag = sb.toString();
+ }
+ } catch (ClassCastException e) {
+ }
+ return null;
+ }
+
+ @Override
public void setProcessLimit(int max) {
enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT,
"setProcessLimit()");
@@ -5553,7 +5610,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return;
}
pr.forcingToForeground = null;
- pr.foregroundServices = false;
+ updateProcessForegroundLocked(pr, false, false);
}
updateOomAdjLocked();
}
@@ -7739,22 +7796,27 @@ public final class ActivityManagerService extends ActivityManagerNative
*/
public void removeContentProvider(IBinder connection, boolean stable) {
enforceNotIsolatedCaller("removeContentProvider");
- synchronized (this) {
- ContentProviderConnection conn;
- try {
- conn = (ContentProviderConnection)connection;
- } catch (ClassCastException e) {
- String msg ="removeContentProvider: " + connection
- + " not a ContentProviderConnection";
- Slog.w(TAG, msg);
- throw new IllegalArgumentException(msg);
- }
- if (conn == null) {
- throw new NullPointerException("connection is null");
- }
- if (decProviderCountLocked(conn, null, null, stable)) {
- updateOomAdjLocked();
+ long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ ContentProviderConnection conn;
+ try {
+ conn = (ContentProviderConnection)connection;
+ } catch (ClassCastException e) {
+ String msg ="removeContentProvider: " + connection
+ + " not a ContentProviderConnection";
+ Slog.w(TAG, msg);
+ throw new IllegalArgumentException(msg);
+ }
+ if (conn == null) {
+ throw new NullPointerException("connection is null");
+ }
+ if (decProviderCountLocked(conn, null, null, stable)) {
+ updateOomAdjLocked();
+ }
}
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
@@ -8361,7 +8423,7 @@ public final class ActivityManagerService extends ActivityManagerNative
mDebugTransient = !persistent;
if (packageName != null) {
forceStopPackageLocked(packageName, -1, false, false, true, true,
- UserHandle.USER_ALL, "set debug app");
+ false, UserHandle.USER_ALL, "set debug app");
}
}
} finally {
@@ -8729,7 +8791,7 @@ public final class ActivityManagerService extends ActivityManagerNative
Context.WINDOW_SERVICE)).addView(v, lp);
}
- public void noteWakeupAlarm(IIntentSender sender) {
+ public void noteWakeupAlarm(IIntentSender sender, int sourceUid, String sourcePkg) {
if (!(sender instanceof PendingIntentRecord)) {
return;
}
@@ -8741,7 +8803,8 @@ public final class ActivityManagerService extends ActivityManagerNative
int MY_UID = Binder.getCallingUid();
int uid = rec.uid == MY_UID ? Process.SYSTEM_UID : rec.uid;
BatteryStatsImpl.Uid.Pkg pkg =
- stats.getPackageStatsLocked(uid, rec.key.packageName);
+ stats.getPackageStatsLocked(sourceUid >= 0 ? sourceUid : uid,
+ sourcePkg != null ? sourcePkg : rec.key.packageName);
pkg.incWakeupsLocked();
}
}
@@ -12112,7 +12175,25 @@ public final class ActivityManagerService extends ActivityManagerNative
if (!brief) {
if (!isCompact) {
pw.print("Total RAM: "); pw.print(memInfo.getTotalSizeKb());
- pw.println(" kB");
+ pw.print(" kB (status ");
+ switch (mLastMemoryLevel) {
+ case ProcessStats.ADJ_MEM_FACTOR_NORMAL:
+ pw.println("normal)");
+ break;
+ case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
+ pw.println("moderate)");
+ break;
+ case ProcessStats.ADJ_MEM_FACTOR_LOW:
+ pw.println("low)");
+ break;
+ case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
+ pw.println("critical)");
+ break;
+ default:
+ pw.print(mLastMemoryLevel);
+ pw.println(")");
+ break;
+ }
pw.print(" Free RAM: "); pw.print(cachedPss + memInfo.getCachedSizeKb()
+ memInfo.getFreeSizeKb()); pw.print(" kB (");
pw.print(cachedPss); pw.print(" cached pss + ");
@@ -12327,7 +12408,7 @@ public final class ActivityManagerService extends ActivityManagerNative
app.unlinkDeathRecipient();
app.makeInactive(mProcessStats);
app.forcingToForeground = null;
- app.foregroundServices = false;
+ updateProcessForegroundLocked(app, false, false);
app.foregroundActivities = false;
app.hasShownUi = false;
app.hasAboveClient = false;
@@ -12456,10 +12537,16 @@ public final class ActivityManagerService extends ActivityManagerNative
startProcessLocked(app, "restart", app.processName);
} else if (app.pid > 0 && app.pid != MY_PID) {
// Goodbye!
+ boolean removed;
synchronized (mPidsSelfLocked) {
mPidsSelfLocked.remove(app.pid);
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
}
+ mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_PROC_FINISH,
+ app.processName, app.info.uid);
+ if (app.isolated) {
+ mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
+ }
app.setPid(0);
}
}
@@ -13286,7 +13373,7 @@ public final class ActivityManagerService extends ActivityManagerNative
String list[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
if (list != null && (list.length > 0)) {
for (String pkg : list) {
- forceStopPackageLocked(pkg, -1, false, true, true, false, userId,
+ forceStopPackageLocked(pkg, -1, false, true, true, false, false, userId,
"storage unmount");
}
sendPackageBroadcastLocked(
@@ -13298,10 +13385,13 @@ public final class ActivityManagerService extends ActivityManagerNative
if (data != null && (ssp=data.getSchemeSpecificPart()) != null) {
boolean removed = Intent.ACTION_PACKAGE_REMOVED.equals(
intent.getAction());
+ boolean fullUninstall = removed &&
+ !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
if (!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false)) {
forceStopPackageLocked(ssp, UserHandle.getAppId(
intent.getIntExtra(Intent.EXTRA_UID, -1)), false, true, true,
- false, userId, removed ? "pkg removed" : "pkg changed");
+ false, fullUninstall, userId,
+ removed ? "pkg removed" : "pkg changed");
}
if (removed) {
sendPackageBroadcastLocked(IApplicationThread.PACKAGE_REMOVED,
@@ -13779,7 +13869,7 @@ public final class ActivityManagerService extends ActivityManagerNative
final long origId = Binder.clearCallingIdentity();
// Instrumentation can kill and relaunch even persistent processes
- forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, userId,
+ forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, false, userId,
"start instr");
ProcessRecord app = addAppLocked(ai, false);
app.instrumentationClass = className;
@@ -13847,7 +13937,7 @@ public final class ActivityManagerService extends ActivityManagerNative
app.instrumentationProfileFile = null;
app.instrumentationArguments = null;
- forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, app.userId,
+ forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, false, app.userId,
"finished inst");
}
@@ -15273,8 +15363,66 @@ public final class ActivityManagerService extends ActivityManagerNative
reportingProcessState, now);
}
+ final void updateProcessForegroundLocked(ProcessRecord proc, boolean isForeground,
+ boolean oomAdj) {
+ if (isForeground != proc.foregroundServices) {
+ proc.foregroundServices = isForeground;
+ ArrayList<ProcessRecord> curProcs = mForegroundPackages.get(proc.info.packageName,
+ proc.info.uid);
+ if (isForeground) {
+ if (curProcs == null) {
+ curProcs = new ArrayList<ProcessRecord>();
+ mForegroundPackages.put(proc.info.packageName, proc.info.uid, curProcs);
+ }
+ if (!curProcs.contains(proc)) {
+ curProcs.add(proc);
+ mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_FOREGROUND_START,
+ proc.info.packageName, proc.info.uid);
+ }
+ } else {
+ if (curProcs != null) {
+ if (curProcs.remove(proc)) {
+ mBatteryStatsService.noteEvent(
+ BatteryStats.HistoryItem.EVENT_FOREGROUND_FINISH,
+ proc.info.packageName, proc.info.uid);
+ if (curProcs.size() <= 0) {
+ mForegroundPackages.remove(proc.info.packageName, proc.info.uid);
+ }
+ }
+ }
+ }
+ if (oomAdj) {
+ updateOomAdjLocked();
+ }
+ }
+ }
+
private final ActivityRecord resumedAppLocked() {
- return mStackSupervisor.resumedAppLocked();
+ ActivityRecord act = mStackSupervisor.resumedAppLocked();
+ String pkg;
+ int uid;
+ if (act != null) {
+ pkg = act.packageName;
+ uid = act.info.applicationInfo.uid;
+ } else {
+ pkg = null;
+ uid = -1;
+ }
+ // Has the UID or resumed package name changed?
+ if (uid != mCurResumedUid || (pkg != mCurResumedPackage
+ && (pkg == null || !pkg.equals(mCurResumedPackage)))) {
+ if (mCurResumedPackage != null) {
+ mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_TOP_FINISH,
+ mCurResumedPackage, mCurResumedUid);
+ }
+ mCurResumedPackage = pkg;
+ mCurResumedUid = uid;
+ if (mCurResumedPackage != null) {
+ mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_TOP_START,
+ mCurResumedPackage, mCurResumedUid);
+ }
+ }
+ return act;
}
final boolean updateOomAdjLocked(ProcessRecord app) {
@@ -15933,8 +16081,20 @@ public final class ActivityManagerService extends ActivityManagerNative
// Multi-user methods
+ /**
+ * Start user, if its not already running, but don't bring it to foreground.
+ */
+ @Override
+ public boolean startUserInBackground(final int userId) {
+ return startUser(userId, /* foreground */ false);
+ }
+
@Override
public boolean switchUser(final int userId) {
+ return startUser(userId, /* foregound */ true);
+ }
+
+ private boolean startUser(final int userId, boolean foreground) {
if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: switchUser() from pid="
@@ -15972,12 +16132,18 @@ public final class ActivityManagerService extends ActivityManagerNative
needStart = true;
}
- mCurrentUserId = userId;
final Integer userIdInt = Integer.valueOf(userId);
mUserLru.remove(userIdInt);
mUserLru.add(userIdInt);
- mWindowManager.setCurrentUser(userId);
+ if (foreground) {
+ mCurrentUserId = userId;
+ mWindowManager.setCurrentUser(userId);
+ } else {
+ final Integer currentUserIdInt = Integer.valueOf(mCurrentUserId);
+ mUserLru.remove(currentUserIdInt);
+ mUserLru.add(currentUserIdInt);
+ }
// Once the internal notion of the active user has switched, we lock the device
// with the option to show the user switcher on the keyguard.
@@ -16002,12 +16168,15 @@ public final class ActivityManagerService extends ActivityManagerNative
needStart = true;
}
- mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
- mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
- mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
- oldUserId, userId, uss));
- mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_TIMEOUT_MSG,
- oldUserId, userId, uss), USER_SWITCH_TIMEOUT);
+ if (foreground) {
+ mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
+ mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
+ mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
+ oldUserId, userId, uss));
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_TIMEOUT_MSG,
+ oldUserId, userId, uss), USER_SWITCH_TIMEOUT);
+ }
+
if (needStart) {
Intent intent = new Intent(Intent.ACTION_USER_STARTED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
@@ -16038,16 +16207,18 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- boolean homeInFront = mStackSupervisor.switchUserLocked(userId, uss);
- if (homeInFront) {
- startHomeActivityLocked(userId);
- } else {
- mStackSupervisor.resumeTopActivitiesLocked();
+ if (foreground) {
+ boolean homeInFront = mStackSupervisor.switchUserLocked(userId, uss);
+ if (homeInFront) {
+ startHomeActivityLocked(userId);
+ } else {
+ mStackSupervisor.resumeTopActivitiesLocked();
+ }
+ EventLogTags.writeAmSwitchUser(userId);
+ getUserManagerLocked().userForeground(userId);
+ sendUserSwitchBroadcastsLocked(oldUserId, userId);
}
- EventLogTags.writeAmSwitchUser(userId);
- getUserManagerLocked().userForeground(userId);
- sendUserSwitchBroadcastsLocked(oldUserId, userId);
if (needStart) {
Intent intent = new Intent(Intent.ACTION_USER_STARTING);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index a27288a..5d23fc3 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -145,6 +145,7 @@ final class ActivityRecord {
private boolean inHistory; // are we in the history stack?
final ActivityStackSupervisor mStackSupervisor;
+ boolean mStartingWindowShown = false;
ActivityContainer mInitialActivityContainer;
void dump(PrintWriter pw, String prefix) {
@@ -597,7 +598,7 @@ final class ActivityRecord {
int requestCode, int resultCode,
Intent resultData) {
ActivityResult r = new ActivityResult(from, resultWho,
- requestCode, resultCode, resultData);
+ requestCode, resultCode, resultData);
if (results == null) {
results = new ArrayList<ResultInfo>();
}
@@ -727,6 +728,10 @@ final class ActivityRecord {
}
}
+ ActivityOptions getOptionsForTargetActivityLocked() {
+ return pendingOptions != null ? pendingOptions.forTargetActivity() : null;
+ }
+
void clearOptionsLocked() {
if (pendingOptions != null) {
pendingOptions.abort();
diff --git a/services/core/java/com/android/server/am/ActivityResult.java b/services/core/java/com/android/server/am/ActivityResult.java
index 6d5bdeb..395918e 100644
--- a/services/core/java/com/android/server/am/ActivityResult.java
+++ b/services/core/java/com/android/server/am/ActivityResult.java
@@ -18,7 +18,6 @@ package com.android.server.am;
import android.app.ResultInfo;
import android.content.Intent;
-import android.os.Bundle;
/**
* Pending result information to send back to an activity.
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 68ef815..922cef4 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -36,6 +36,8 @@ import static com.android.server.am.ActivityStackSupervisor.DEBUG_SAVED_STATE;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_STATES;
import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
+import android.os.Trace;
+import android.util.Log;
import com.android.internal.os.BatteryStatsImpl;
import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerService.ItemMatcher;
@@ -121,8 +123,7 @@ final class ActivityStack {
// convertToTranslucent().
static final long TRANSLUCENT_CONVERSION_TIMEOUT = 2000;
- static final boolean SCREENSHOT_FORCE_565 = ActivityManager
- .isLowRamDeviceStatic() ? true : false;
+ static final boolean SCREENSHOT_FORCE_565 = ActivityManager.isLowRamDeviceStatic();
enum ActivityState {
INITIALIZING,
@@ -401,8 +402,9 @@ final class ActivityStack {
// be simplified once we stop storing tasks with empty mActivities lists.
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- return activities.get(activityNdx);
+ final int topActivityNdx = activities.size() - 1;
+ if (topActivityNdx >= 0) {
+ return activities.get(topActivityNdx);
}
}
return null;
@@ -1107,7 +1109,7 @@ final class ActivityStack {
mWindowManager.setAppVisibility(r.appToken, true);
}
if (r != starting) {
- mStackSupervisor.startSpecificActivityLocked(r, false, false);
+ mStackSupervisor.startSpecificActivityLocked(r, false, false, null);
}
}
@@ -1246,6 +1248,32 @@ final class ActivityStack {
}
}
+ /** If any activities below the top running one are in the INITIALIZING state and they have a
+ * starting window displayed then remove that starting window. It is possible that the activity
+ * in this state will never resumed in which case that starting window will be orphaned. */
+ void cancelInitializingActivities() {
+ final ActivityRecord topActivity = topRunningActivityLocked(null);
+ boolean aboveTop = true;
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = activities.get(activityNdx);
+ if (aboveTop) {
+ if (r == topActivity) {
+ aboveTop = false;
+ }
+ continue;
+ }
+
+ if (r.state == ActivityState.INITIALIZING && r.mStartingWindowShown) {
+ if (DEBUG_VISBILITY) Slog.w(TAG, "Found orphaned starting window " + r);
+ r.mStartingWindowShown = false;
+ mWindowManager.removeAppStartingWindow(r.appToken);
+ }
+ }
+ }
+ }
+
/**
* Ensure that the top activity in the stack is resumed.
*
@@ -1269,6 +1297,8 @@ final class ActivityStack {
return false;
}
+ cancelInitializingActivities();
+
// Find the first activity that is not finishing.
ActivityRecord next = topRunningActivityLocked(null);
@@ -1514,7 +1544,13 @@ final class ActivityStack {
mWindowManager.prepareAppTransition(AppTransition.TRANSIT_ACTIVITY_OPEN, false);
}
}
+
+ Bundle resumeAnimOptions = null;
if (anim) {
+ ActivityOptions opts = next.getOptionsForTargetActivityLocked();
+ if (opts != null) {
+ resumeAnimOptions = opts.toBundle();
+ }
next.applyOptionsLocked();
} else {
next.clearOptionsLocked();
@@ -1607,7 +1643,7 @@ final class ActivityStack {
next.app.pendingUiClean = true;
next.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_TOP);
next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
- mService.isNextTransitionForward());
+ mService.isNextTransitionForward(), resumeAnimOptions);
mStackSupervisor.checkReadyForSleepLocked();
@@ -1631,7 +1667,7 @@ final class ActivityStack {
next.nonLocalizedLabel, next.labelRes, next.icon, next.logo,
next.windowFlags, null, true);
}
- mStackSupervisor.startSpecificActivityLocked(next, true, false);
+ mStackSupervisor.startSpecificActivityLocked(next, true, false, resumeAnimOptions);
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
@@ -1669,7 +1705,7 @@ final class ActivityStack {
if (DEBUG_SWITCH) Slog.v(TAG, "Restarting: " + next);
}
if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Restarting " + next);
- mStackSupervisor.startSpecificActivityLocked(next, true, true);
+ mStackSupervisor.startSpecificActivityLocked(next, true, true, resumeAnimOptions);
}
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
@@ -1832,6 +1868,7 @@ final class ActivityStack {
r.info.applicationInfo), r.nonLocalizedLabel,
r.labelRes, r.icon, r.logo, r.windowFlags,
prev != null ? prev.appToken : null, showStartingIcon);
+ r.mStartingWindowShown = true;
}
} else {
// If this is the first activity, don't do any fancy animations,
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index ad6ff0e..9636de7 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -406,7 +406,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
if (hr.app == null && app.uid == hr.info.applicationInfo.uid
&& processName.equals(hr.processName)) {
try {
- if (realStartActivityLocked(hr, app, true, true)) {
+ if (realStartActivityLocked(hr, app, true, true, null)) {
didSomething = true;
}
} catch (Exception e) {
@@ -523,7 +523,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
void pauseChildStacks(ActivityRecord parent, boolean userLeaving, boolean uiSleeping) {
- // TODO: Put all stacks in supervisor and iterate through them instead.
+ // TODO: Put all stacks in supervisor and iterate through them instead.
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
@@ -930,7 +930,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
final boolean realStartActivityLocked(ActivityRecord r,
- ProcessRecord app, boolean andResume, boolean checkConfig)
+ ProcessRecord app, boolean andResume, boolean checkConfig, Bundle resumeArgs)
throws RemoteException {
r.startFreezingScreenLocked(app, 0);
@@ -1022,13 +1022,14 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
}
}
+
app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_TOP);
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info,
new Configuration(mService.mConfiguration), r.compat,
app.repProcState, r.icicle, results, newIntents, !andResume,
mService.isNextTransitionForward(), profileFile, profileFd,
- profileAutoStop);
+ profileAutoStop, resumeArgs);
if ((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
// This may be a heavy-weight process! Note that the package
@@ -1102,7 +1103,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
void startSpecificActivityLocked(ActivityRecord r,
- boolean andResume, boolean checkConfig) {
+ boolean andResume, boolean checkConfig, Bundle resumeArgs) {
// Is this activity's application already running?
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
r.info.applicationInfo.uid, true);
@@ -1119,7 +1120,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
// separate apk in the process.
app.addPackage(r.info.packageName, mService.mProcessStats);
}
- realStartActivityLocked(r, app, andResume, checkConfig);
+ realStartActivityLocked(r, app, andResume, checkConfig, resumeArgs);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting activity "
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index ff06513..fc7aac2 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -105,10 +105,32 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
return data;
}
- public void noteStartWakelock(int uid, int pid, String name, int type) {
+ public void addIsolatedUid(int isolatedUid, int appUid) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.noteStartWakeLocked(uid, pid, name, type);
+ mStats.addIsolatedUidLocked(isolatedUid, appUid);
+ }
+ }
+
+ public void removeIsolatedUid(int isolatedUid, int appUid) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.removeIsolatedUidLocked(isolatedUid, appUid);
+ }
+ }
+
+ public void noteEvent(int code, String name, int uid) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteEventLocked(code, name, uid);
+ }
+ }
+
+ public void noteStartWakelock(int uid, int pid, String name, String historyName, int type,
+ boolean unimportantForLogging) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteStartWakeLocked(uid, pid, name, historyName, type, unimportantForLogging);
}
}
@@ -119,10 +141,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
}
}
- public void noteStartWakelockFromSource(WorkSource ws, int pid, String name, int type) {
+ public void noteStartWakelockFromSource(WorkSource ws, int pid, String name,
+ String historyName, int type, boolean unimportantForLogging) {
enforceCallingPermission();
synchronized (mStats) {
- mStats.noteStartWakeFromSourceLocked(ws, pid, name, type);
+ mStats.noteStartWakeFromSourceLocked(ws, pid, name, historyName,
+ type, unimportantForLogging);
}
}
@@ -207,7 +231,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
mStats.noteUserActivityLocked(uid, event);
}
}
-
+
+ public void noteDataConnectionActive(int type, boolean active) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteDataConnectionActive(type, active);
+ }
+ }
+
public void notePhoneOn() {
enforceCallingPermission();
synchronized (mStats) {
@@ -307,6 +338,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
}
}
+ public void noteWifiState(int wifiState, String accessPoint) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteWifiStateLocked(wifiState, accessPoint);
+ }
+ }
+
public void noteBluetoothOn() {
enforceCallingPermission();
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -350,6 +388,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
}
}
+ public void noteBluetoothState(int bluetoothState) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteBluetoothStateLocked(bluetoothState);
+ }
+ }
+
public void noteFullWifiLockAcquired(int uid) {
enforceCallingPermission();
synchronized (mStats) {
@@ -496,8 +541,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
private void dumpHelp(PrintWriter pw) {
pw.println("Battery stats (batterystats) dump options:");
- pw.println(" [--checkin] [-c] [--unplugged] [--reset] [--write] [-h] [<package.name>]");
+ pw.println(" [--checkin] [--history] [-c] [--unplugged] [--reset] [--write]");
+ pw.println(" [-h] [<package.name>]");
pw.println(" --checkin: format output for a checkin report.");
+ pw.println(" --history: show only history data.");
pw.println(" --unplugged: only output data since last unplugged.");
pw.println(" --reset: reset the stats, clearing all current data.");
pw.println(" --write: force write current collected stats to disk.");
@@ -517,6 +564,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
boolean isCheckin = false;
boolean includeHistory = false;
+ boolean historyOnly = false;
boolean isUnpluggedOnly = false;
boolean noOutput = false;
int reqUid = -1;
@@ -524,6 +572,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
for (String arg : args) {
if ("--checkin".equals(arg)) {
isCheckin = true;
+ } else if ("--history".equals(arg)) {
+ historyOnly = true;
} else if ("-c".equals(arg)) {
isCheckin = true;
includeHistory = true;
@@ -531,7 +581,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
isUnpluggedOnly = true;
} else if ("--reset".equals(arg)) {
synchronized (mStats) {
- mStats.resetAllStatsLocked();
+ mStats.resetAllStatsCmdLocked();
pw.println("Battery stats reset.");
noOutput = true;
}
@@ -569,11 +619,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
if (isCheckin) {
List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(0);
synchronized (mStats) {
- mStats.dumpCheckinLocked(pw, apps, isUnpluggedOnly, includeHistory);
+ mStats.dumpCheckinLocked(mContext, pw, apps, isUnpluggedOnly, includeHistory,
+ historyOnly);
}
} else {
synchronized (mStats) {
- mStats.dumpLocked(pw, isUnpluggedOnly, reqUid);
+ mStats.dumpLocked(mContext, pw, isUnpluggedOnly, reqUid, historyOnly);
}
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index aef9e5c..b15fa5d 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -27,7 +27,6 @@ import android.content.ComponentName;
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.pm.ActivityInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
diff --git a/services/core/java/com/android/server/am/ContentProviderRecord.java b/services/core/java/com/android/server/am/ContentProviderRecord.java
index 646b7d2..ff22764 100644
--- a/services/core/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/core/java/com/android/server/am/ContentProviderRecord.java
@@ -31,7 +31,6 @@ import android.util.Slog;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.HashSet;
final class ContentProviderRecord {
final ActivityManagerService service;
diff --git a/services/core/java/com/android/server/am/IntentBindRecord.java b/services/core/java/com/android/server/am/IntentBindRecord.java
index 21cf266..ba6010a 100644
--- a/services/core/java/com/android/server/am/IntentBindRecord.java
+++ b/services/core/java/com/android/server/am/IntentBindRecord.java
@@ -22,8 +22,6 @@ import android.os.IBinder;
import android.util.ArrayMap;
import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.Iterator;
/**
* A particular Intent that has been bound to a Service.
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 00fa216..98999e9 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -41,6 +41,8 @@ final class PendingIntentRecord extends IIntentSender.Stub {
boolean canceled = false;
String stringName;
+ String lastTagPrefix;
+ String lastTag;
final static class Key {
final int type;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index f5920c8..755a237 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -16,7 +16,6 @@
package com.android.server.am;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 217a8d6..10574ed 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -59,6 +59,7 @@ final class ProcessRecord {
// 'persistent' is true (in which case we
// are in the process of launching the app)
ProcessStats.ProcessState baseProcessTracker;
+ BatteryStatsImpl.Uid.Proc curProcBatteryStats;
int pid; // The process of this application; 0 if none
boolean starting; // True if the process is being started
long lastActivityTime; // For managing the LRU list
@@ -391,14 +392,15 @@ final class ProcessRecord {
origBase.makeInactive();
}
baseProcessTracker = tracker.getProcessStateLocked(info.packageName, info.uid,
- processName);
+ info.versionCode, processName);
baseProcessTracker.makeActive();
for (int i=0; i<pkgList.size(); i++) {
ProcessStats.ProcessState ps = pkgList.valueAt(i);
if (ps != null && ps != origBase) {
ps.makeInactive();
}
- ps = tracker.getProcessStateLocked(pkgList.keyAt(i), info.uid, processName);
+ ps = tracker.getProcessStateLocked(pkgList.keyAt(i), info.uid,
+ info.versionCode, processName);
if (ps != baseProcessTracker) {
ps.makeActive();
}
@@ -571,7 +573,7 @@ final class ProcessRecord {
if (!pkgList.containsKey(pkg)) {
if (baseProcessTracker != null) {
ProcessStats.ProcessState state = tracker.getProcessStateLocked(
- pkg, info.uid, processName);
+ pkg, info.uid, info.versionCode, processName);
pkgList.put(pkg, state);
if (state != baseProcessTracker) {
state.makeActive();
@@ -618,7 +620,7 @@ final class ProcessRecord {
}
pkgList.clear();
ProcessStats.ProcessState ps = tracker.getProcessStateLocked(
- info.packageName, info.uid, processName);
+ info.packageName, info.uid, info.versionCode, processName);
pkgList.put(info.packageName, ps);
if (ps != baseProcessTracker) {
ps.makeActive();
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index e05fcda..14f3ef9 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -16,8 +16,6 @@
package com.android.server.am;
-import android.app.AppGlobals;
-import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Parcel;
@@ -25,7 +23,6 @@ import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Slog;
@@ -111,13 +108,14 @@ public final class ProcessStatsService extends IProcessStats.Stub {
}
public ProcessStats.ProcessState getProcessStateLocked(String packageName,
- int uid, String processName) {
- return mProcessStats.getProcessStateLocked(packageName, uid, processName);
+ int uid, int versionCode, String processName) {
+ return mProcessStats.getProcessStateLocked(packageName, uid, versionCode, processName);
}
public ProcessStats.ServiceState getServiceStateLocked(String packageName, int uid,
- String processName, String className) {
- return mProcessStats.getServiceStateLocked(packageName, uid, processName, className);
+ int versionCode, String processName, String className) {
+ return mProcessStats.getServiceStateLocked(packageName, uid, versionCode, processName,
+ className);
}
public boolean isMemFactorLowered() {
@@ -137,25 +135,29 @@ public final class ProcessStatsService extends IProcessStats.Stub {
}
mProcessStats.mMemFactor = memFactor;
mProcessStats.mStartTime = now;
- ArrayMap<String, SparseArray<ProcessStats.PackageState>> pmap
+ final ArrayMap<String, SparseArray<SparseArray<ProcessStats.PackageState>>> pmap
= mProcessStats.mPackages.getMap();
- for (int i=0; i<pmap.size(); i++) {
- SparseArray<ProcessStats.PackageState> uids = pmap.valueAt(i);
- for (int j=0; j<uids.size(); j++) {
- ProcessStats.PackageState pkg = uids.valueAt(j);
- ArrayMap<String, ProcessStats.ServiceState> services = pkg.mServices;
- for (int k=0; k<services.size(); k++) {
- ProcessStats.ServiceState service = services.valueAt(k);
- if (service.isInUse()) {
- if (service.mStartedState != ProcessStats.STATE_NOTHING) {
- service.setStarted(true, memFactor, now);
- }
- if (service.mBoundState != ProcessStats.STATE_NOTHING) {
- service.setBound(true, memFactor, now);
- }
- if (service.mExecState != ProcessStats.STATE_NOTHING) {
- service.setExecuting(true, memFactor, now);
+ for (int ipkg=pmap.size()-1; ipkg>=0; ipkg--) {
+ final SparseArray<SparseArray<ProcessStats.PackageState>> uids = pmap.valueAt(ipkg);
+ for (int iuid=uids.size()-1; iuid>=0; iuid--) {
+ final SparseArray<ProcessStats.PackageState> vers = uids.valueAt(iuid);
+ for (int iver=vers.size()-1; iver>=0; iver--) {
+ final ProcessStats.PackageState pkg = vers.valueAt(iver);
+ final ArrayMap<String, ProcessStats.ServiceState> services = pkg.mServices;
+ for (int isvc=services.size()-1; isvc>=0; isvc--) {
+ final ProcessStats.ServiceState service = services.valueAt(isvc);
+ if (service.isInUse()) {
+ if (service.mStartedState != ProcessStats.STATE_NOTHING) {
+ service.setStarted(true, memFactor, now);
+ }
+ if (service.mBoundState != ProcessStats.STATE_NOTHING) {
+ service.setBound(true, memFactor, now);
+ }
+ if (service.mExecState != ProcessStats.STATE_NOTHING) {
+ service.setExecuting(true, memFactor, now);
+ }
}
+
}
}
}
@@ -294,25 +296,32 @@ public final class ProcessStatsService extends IProcessStats.Stub {
Slog.w(TAG, " Uid " + uids.keyAt(iu) + ": " + uids.valueAt(iu));
}
}
- ArrayMap<String, SparseArray<ProcessStats.PackageState>> pkgMap
+ ArrayMap<String, SparseArray<SparseArray<ProcessStats.PackageState>>> pkgMap
= stats.mPackages.getMap();
final int NPKG = pkgMap.size();
for (int ip=0; ip<NPKG; ip++) {
Slog.w(TAG, "Package: " + pkgMap.keyAt(ip));
- SparseArray<ProcessStats.PackageState> uids = pkgMap.valueAt(ip);
+ SparseArray<SparseArray<ProcessStats.PackageState>> uids
+ = pkgMap.valueAt(ip);
final int NUID = uids.size();
for (int iu=0; iu<NUID; iu++) {
Slog.w(TAG, " Uid: " + uids.keyAt(iu));
- ProcessStats.PackageState pkgState = uids.valueAt(iu);
- final int NPROCS = pkgState.mProcesses.size();
- for (int iproc=0; iproc<NPROCS; iproc++) {
- Slog.w(TAG, " Process " + pkgState.mProcesses.keyAt(iproc)
- + ": " + pkgState.mProcesses.valueAt(iproc));
- }
- final int NSRVS = pkgState.mServices.size();
- for (int isvc=0; isvc<NSRVS; isvc++) {
- Slog.w(TAG, " Service " + pkgState.mServices.keyAt(isvc)
- + ": " + pkgState.mServices.valueAt(isvc));
+ SparseArray<ProcessStats.PackageState> vers = uids.valueAt(iu);
+ final int NVERS = vers.size();
+ for (int iv=0; iv<NVERS; iv++) {
+ Slog.w(TAG, " Vers: " + vers.keyAt(iv));
+ ProcessStats.PackageState pkgState = vers.valueAt(iv);
+ final int NPROCS = pkgState.mProcesses.size();
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ Slog.w(TAG, " Process " + pkgState.mProcesses.keyAt(iproc)
+ + ": " + pkgState.mProcesses.valueAt(iproc));
+ }
+ final int NSRVS = pkgState.mServices.size();
+ for (int isvc=0; isvc<NSRVS; isvc++) {
+ Slog.w(TAG, " Service " + pkgState.mServices.keyAt(isvc)
+ + ": " + pkgState.mServices.valueAt(isvc));
+
+ }
}
}
}
diff --git a/services/core/java/com/android/server/am/ReceiverList.java b/services/core/java/com/android/server/am/ReceiverList.java
index fa8c1df..6ade736 100644
--- a/services/core/java/com/android/server/am/ReceiverList.java
+++ b/services/core/java/com/android/server/am/ReceiverList.java
@@ -17,11 +17,8 @@
package com.android.server.am;
import android.content.IIntentReceiver;
-import android.content.Intent;
import android.os.Binder;
-import android.os.Bundle;
import android.os.IBinder;
-import android.os.RemoteException;
import android.util.PrintWriterPrinter;
import android.util.Printer;
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index cb04835..363a9b7 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -329,7 +329,8 @@ final class ServiceRecord extends Binder {
}
if ((serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) {
tracker = ams.mProcessStats.getServiceStateLocked(serviceInfo.packageName,
- serviceInfo.applicationInfo.uid, serviceInfo.processName, serviceInfo.name);
+ serviceInfo.applicationInfo.uid, serviceInfo.applicationInfo.versionCode,
+ serviceInfo.processName, serviceInfo.name);
tracker.applyNewOwner(this);
}
return tracker;
@@ -346,7 +347,8 @@ final class ServiceRecord extends Binder {
if (restartTracker == null) {
if ((serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) {
restartTracker = ams.mProcessStats.getServiceStateLocked(serviceInfo.packageName,
- serviceInfo.applicationInfo.uid, serviceInfo.processName, serviceInfo.name);
+ serviceInfo.applicationInfo.uid, serviceInfo.applicationInfo.versionCode,
+ serviceInfo.processName, serviceInfo.name);
}
if (restartTracker == null) {
return;
diff --git a/services/core/java/com/android/server/am/UsageStatsService.java b/services/core/java/com/android/server/am/UsageStatsService.java
index 09cb344..587f949 100644
--- a/services/core/java/com/android/server/am/UsageStatsService.java
+++ b/services/core/java/com/android/server/am/UsageStatsService.java
@@ -20,7 +20,6 @@ import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.IPackageManager;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.IBinder;
@@ -54,7 +53,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 6aa596d..f47d66d 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -31,7 +31,6 @@ import android.content.IntentFilter;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
@@ -40,7 +39,6 @@ import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java
index 7786fe6..8815d0f 100644
--- a/services/core/java/com/android/server/connectivity/PacManager.java
+++ b/services/core/java/com/android/server/connectivity/PacManager.java
@@ -24,16 +24,13 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.net.Proxy;
import android.net.ProxyProperties;
-import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index adf1dfc..b233943 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -28,7 +28,6 @@ import android.content.res.Resources;
import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager;
import android.net.IConnectivityManager;
-import android.net.INetworkManagementEventObserver;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
@@ -52,7 +51,6 @@ import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.IoThread;
import com.android.server.net.BaseNetworkObserver;
-import com.google.android.collect.Lists;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -472,6 +470,7 @@ public class Tethering extends BaseNetworkObserver {
mTetheredNotification.defaults &= ~Notification.DEFAULT_SOUND;
mTetheredNotification.flags = Notification.FLAG_ONGOING_EVENT;
mTetheredNotification.tickerText = title;
+ mTetheredNotification.visibility = Notification.VISIBILITY_PUBLIC;
mTetheredNotification.setLatestEventInfo(mContext, title, message, pi);
notificationManager.notifyAsUser(null, mTetheredNotification.icon,
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 2ca2cc5..f4bad73 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -29,7 +29,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
@@ -45,6 +44,7 @@ import android.net.LinkProperties;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.net.NetworkInfo;
+import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.NetworkInfo.DetailedState;
import android.os.Binder;
@@ -63,25 +63,22 @@ import android.security.Credentials;
import android.security.KeyStore;
import android.util.Log;
import android.util.SparseBooleanArray;
-import android.widget.Toast;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.R;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
-import com.android.internal.util.Preconditions;
import com.android.server.ConnectivityService.VpnCallback;
import com.android.server.net.BaseNetworkObserver;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
-import java.net.Inet4Address;
import java.net.InetAddress;
+import java.net.Inet4Address;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
-import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import libcore.io.IoUtils;
@@ -282,13 +279,12 @@ public class Vpn extends BaseNetworkStateTracker {
}
/**
- * Protect a socket from routing changes by binding it to the given
- * interface. The socket is NOT closed by this method.
+ * Protect a socket from VPN rules by binding it to the main routing table.
+ * The socket is NOT closed by this method.
*
* @param socket The socket to be bound.
- * @param interfaze The name of the interface.
*/
- public void protect(ParcelFileDescriptor socket, String interfaze) throws Exception {
+ public void protect(ParcelFileDescriptor socket) throws Exception {
PackageManager pm = mContext.getPackageManager();
int appUid = pm.getPackageUid(mPackage, mUserId);
@@ -302,8 +298,6 @@ public class Vpn extends BaseNetworkStateTracker {
} finally {
Binder.restoreCallingIdentity(token);
}
- // bind the socket to the interface
- jniProtect(socket.getFd(), interfaze);
}
@@ -435,6 +429,18 @@ public class Vpn extends BaseNetworkStateTracker {
return tun;
}
+ /**
+ * Check if a given address is covered by the VPN's routing rules.
+ */
+ public boolean isAddressCovered(InetAddress address) {
+ synchronized (Vpn.this) {
+ if (!isRunningLocked()) {
+ return false;
+ }
+ return RouteInfo.selectBestRoute(mConfig.routes, address) != null;
+ }
+ }
+
private boolean isRunningLocked() {
return mVpnUsers != null;
}
@@ -670,7 +676,6 @@ public class Vpn extends BaseNetworkStateTracker {
private native int jniSetRoutes(String interfaze, String routes);
private native void jniReset(String interfaze);
private native int jniCheck(String interfaze);
- private native void jniProtect(int socket, String interfaze);
private static RouteInfo findIPv4DefaultRoute(LinkProperties prop) {
for (RouteInfo route : prop.getAllRoutes()) {
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 023bf2b..c7d2871 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -19,11 +19,13 @@ package com.android.server.content;
import android.Manifest;
import android.accounts.Account;
import android.app.ActivityManager;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.IContentService;
import android.content.ISyncStatusObserver;
import android.content.PeriodicSync;
+import android.content.pm.PackageManager;
import android.content.SyncAdapterType;
import android.content.SyncInfo;
import android.content.SyncRequest;
@@ -41,7 +43,6 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
-import android.util.Pair;
import android.util.Slog;
import android.util.SparseIntArray;
@@ -315,7 +316,6 @@ public final class ContentService extends IContentService.Stub {
}
}
- @Override
public void requestSync(Account account, String authority, Bundle extras) {
ContentResolver.validateSyncExtrasBundle(extras);
int userId = UserHandle.getCallingUserId();
@@ -345,46 +345,56 @@ public final class ContentService extends IContentService.Stub {
* Depending on the request, we enqueue to suit in the SyncManager.
* @param request The request object. Validation of this object is done by its builder.
*/
- @Override
public void sync(SyncRequest request) {
- Bundle extras = request.getBundle();
- long flextime = request.getSyncFlexTime();
- long runAtTime = request.getSyncRunTime();
int userId = UserHandle.getCallingUserId();
- int uId = Binder.getCallingUid();
-
+ int callerUid = Binder.getCallingUid();
// This makes it so that future permission checks will be in the context of this
// process rather than the caller's process. We will restore this before returning.
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
- if (syncManager != null) {
+ if (syncManager == null) {
+ return;
+ }
+
+ Bundle extras = request.getBundle();
+ long flextime = request.getSyncFlexTime();
+ long runAtTime = request.getSyncRunTime();
+ if (request.isPeriodic()) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.WRITE_SYNC_SETTINGS,
+ "no permission to write the sync settings");
+ SyncStorageEngine.EndPoint info;
+ if (!request.hasAuthority()) {
+ // Extra permissions checking for sync service.
+ verifySignatureForPackage(callerUid,
+ request.getService().getPackageName(), "sync");
+ info = new SyncStorageEngine.EndPoint(request.getService(), userId);
+ } else {
+ info = new SyncStorageEngine.EndPoint(
+ request.getAccount(), request.getProvider(), userId);
+ }
+ if (runAtTime < 60) {
+ Slog.w(TAG, "Requested poll frequency of " + runAtTime
+ + " seconds being rounded up to 60 seconds.");
+ runAtTime = 60;
+ }
+ // Schedule periodic sync.
+ getSyncManager().getSyncStorageEngine()
+ .updateOrAddPeriodicSync(info, runAtTime, flextime, extras);
+ } else {
+ long beforeRuntimeMillis = (flextime) * 1000;
+ long runtimeMillis = runAtTime * 1000;
if (request.hasAuthority()) {
- // Sync Adapter registered with the system - old API.
- final Account account = request.getAccount();
- final String provider = request.getProvider();
- if (request.isPeriodic()) {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.WRITE_SYNC_SETTINGS,
- "no permission to write the sync settings");
- if (runAtTime < 60) {
- Slog.w(TAG, "Requested poll frequency of " + runAtTime
- + " seconds being rounded up to 60 seconds.");
- runAtTime = 60;
- }
- PeriodicSync syncToAdd =
- new PeriodicSync(account, provider, extras, runAtTime, flextime);
- getSyncManager().getSyncStorageEngine().addPeriodicSync(syncToAdd, userId);
- } else {
- long beforeRuntimeMillis = (flextime) * 1000;
- long runtimeMillis = runAtTime * 1000;
- syncManager.scheduleSync(
- account, userId, uId, provider, extras,
- beforeRuntimeMillis, runtimeMillis,
- false /* onlyThoseWithUnknownSyncableState */);
- }
+ syncManager.scheduleSync(
+ request.getAccount(), userId, callerUid, request.getProvider(), extras,
+ beforeRuntimeMillis, runtimeMillis,
+ false /* onlyThoseWithUnknownSyncableState */);
} else {
- Log.w(TAG, "Unrecognised sync parameters, doing nothing.");
+ syncManager.scheduleSync(
+ request.getService(), userId, callerUid, extras,
+ beforeRuntimeMillis,
+ runtimeMillis); // Empty function.
}
}
} finally {
@@ -395,11 +405,14 @@ public final class ContentService extends IContentService.Stub {
/**
* Clear all scheduled sync operations that match the uri and cancel the active sync
* if they match the authority and account, if they are present.
- * @param account filter the pending and active syncs to cancel using this account
- * @param authority filter the pending and active syncs to cancel using this authority
+ *
+ * @param account filter the pending and active syncs to cancel using this account, or null.
+ * @param authority filter the pending and active syncs to cancel using this authority, or
+ * null.
+ * @param cname cancel syncs running on this service, or null for provider/account.
*/
@Override
- public void cancelSync(Account account, String authority) {
+ public void cancelSync(Account account, String authority, ComponentName cname) {
if (authority != null && authority.length() == 0) {
throw new IllegalArgumentException("Authority must be non-empty");
}
@@ -411,9 +424,49 @@ public final class ContentService extends IContentService.Stub {
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
- syncManager.clearScheduledSyncOperations(account, userId, authority);
- syncManager.cancelActiveSync(account, userId, authority);
+ SyncStorageEngine.EndPoint info;
+ if (cname == null) {
+ info = new SyncStorageEngine.EndPoint(account, authority, userId);
+ } else {
+ info = new SyncStorageEngine.EndPoint(cname, userId);
+ }
+ syncManager.clearScheduledSyncOperations(info);
+ syncManager.cancelActiveSync(info, null /* all syncs for this adapter */);
+ }
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ public void cancelRequest(SyncRequest request) {
+ SyncManager syncManager = getSyncManager();
+ if (syncManager == null) return;
+ int userId = UserHandle.getCallingUserId();
+ int callerUid = Binder.getCallingUid();
+
+ long identityToken = clearCallingIdentity();
+ try {
+ SyncStorageEngine.EndPoint info;
+ Bundle extras = new Bundle(request.getBundle());
+ if (request.hasAuthority()) {
+ Account account = request.getAccount();
+ String provider = request.getProvider();
+ info = new SyncStorageEngine.EndPoint(account, provider, userId);
+ } else {
+ // Only allowed to manipulate syncs for a service which you own.
+ ComponentName service = request.getService();
+ verifySignatureForPackage(callerUid, service.getPackageName(), "cancel");
+ info = new SyncStorageEngine.EndPoint(service, userId);
}
+ if (request.isPeriodic()) {
+ // Remove periodic sync.
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
+ "no permission to write the sync settings");
+ getSyncManager().getSyncStorageEngine().removePeriodicSync(info, extras);
+ }
+ // Cancel active syncs and clear pending syncs from the queue.
+ syncManager.cancelScheduledSyncOperation(info, extras);
+ syncManager.cancelActiveSync(info, extras);
} finally {
restoreCallingIdentity(identityToken);
}
@@ -447,8 +500,8 @@ public final class ContentService extends IContentService.Stub {
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
- return syncManager.getSyncStorageEngine().getSyncAutomatically(
- account, userId, providerName);
+ return syncManager.getSyncStorageEngine()
+ .getSyncAutomatically(account, userId, providerName);
}
} finally {
restoreCallingIdentity(identityToken);
@@ -469,8 +522,8 @@ public final class ContentService extends IContentService.Stub {
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
- syncManager.getSyncStorageEngine().setSyncAutomatically(
- account, userId, providerName, sync);
+ syncManager.getSyncStorageEngine()
+ .setSyncAutomatically(account, userId, providerName, sync);
}
} finally {
restoreCallingIdentity(identityToken);
@@ -496,21 +549,22 @@ public final class ContentService extends IContentService.Stub {
+ " seconds being rounded up to 60 seconds.");
pollFrequency = 60;
}
+ long defaultFlex = SyncStorageEngine.calculateDefaultFlexTime(pollFrequency);
long identityToken = clearCallingIdentity();
try {
- // Add default flex time to this sync.
- PeriodicSync syncToAdd =
- new PeriodicSync(account, authority, extras,
- pollFrequency,
- SyncStorageEngine.calculateDefaultFlexTime(pollFrequency));
- getSyncManager().getSyncStorageEngine().addPeriodicSync(syncToAdd, userId);
+ SyncStorageEngine.EndPoint info =
+ new SyncStorageEngine.EndPoint(account, authority, userId);
+ getSyncManager().getSyncStorageEngine()
+ .updateOrAddPeriodicSync(info,
+ pollFrequency,
+ defaultFlex,
+ extras);
} finally {
restoreCallingIdentity(identityToken);
}
}
- @Override
public void removePeriodicSync(Account account, String authority, Bundle extras) {
if (account == null) {
throw new IllegalArgumentException("Account must not be null");
@@ -524,24 +578,18 @@ public final class ContentService extends IContentService.Stub {
int userId = UserHandle.getCallingUserId();
long identityToken = clearCallingIdentity();
try {
- PeriodicSync syncToRemove = new PeriodicSync(account, authority, extras,
- 0 /* Not read for removal */, 0 /* Not read for removal */);
- getSyncManager().getSyncStorageEngine().removePeriodicSync(syncToRemove, userId);
+ getSyncManager().getSyncStorageEngine()
+ .removePeriodicSync(
+ new SyncStorageEngine.EndPoint(account, authority, userId),
+ extras);
} finally {
restoreCallingIdentity(identityToken);
}
}
- /**
- * TODO: Implement.
- * @param request Sync to remove.
- */
- public void removeSync(SyncRequest request) {
- }
-
- @Override
- public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName) {
+ public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName,
+ ComponentName cname) {
if (account == null) {
throw new IllegalArgumentException("Account must not be null");
}
@@ -551,11 +599,20 @@ public final class ContentService extends IContentService.Stub {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
"no permission to read the sync settings");
+ int callerUid = Binder.getCallingUid();
int userId = UserHandle.getCallingUserId();
long identityToken = clearCallingIdentity();
try {
- return getSyncManager().getSyncStorageEngine().getPeriodicSyncs(
- account, userId, providerName);
+ if (cname == null) {
+ return getSyncManager().getSyncStorageEngine().getPeriodicSyncs(
+ new SyncStorageEngine.EndPoint(account, providerName, userId));
+ } else if (account == null && providerName == null) {
+ verifySignatureForPackage(callerUid, cname.getPackageName(), "getPeriodicSyncs");
+ return getSyncManager().getSyncStorageEngine().getPeriodicSyncs(
+ new SyncStorageEngine.EndPoint(cname, userId));
+ } else {
+ throw new IllegalArgumentException("Invalid authority specified");
+ }
} finally {
restoreCallingIdentity(identityToken);
}
@@ -579,7 +636,6 @@ public final class ContentService extends IContentService.Stub {
return -1;
}
- @Override
public void setIsSyncable(Account account, String providerName, int syncable) {
if (TextUtils.isEmpty(providerName)) {
throw new IllegalArgumentException("Authority must not be empty");
@@ -600,6 +656,45 @@ public final class ContentService extends IContentService.Stub {
}
}
+ public void setServiceActive(ComponentName cname, boolean active) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
+ "no permission to write the sync settings");
+ verifySignatureForPackage(Binder.getCallingUid(), cname.getPackageName(),
+ "setServiceActive");
+
+ int userId = UserHandle.getCallingUserId();
+ long identityToken = clearCallingIdentity();
+ try {
+ SyncManager syncManager = getSyncManager();
+ if (syncManager != null) {
+ syncManager.getSyncStorageEngine().setIsTargetServiceActive(
+ cname, userId, active);
+ }
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ public boolean isServiceActive(ComponentName cname) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
+ "no permission to read the sync settings");
+ verifySignatureForPackage(Binder.getCallingUid(), cname.getPackageName(),
+ "isServiceActive");
+
+ int userId = UserHandle.getCallingUserId();
+ long identityToken = clearCallingIdentity();
+ try {
+ SyncManager syncManager = getSyncManager();
+ if (syncManager != null) {
+ return syncManager.getSyncStorageEngine()
+ .getIsTargetServiceActive(cname, userId);
+ }
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ return false;
+ }
+
@Override
public boolean getMasterSyncAutomatically() {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
@@ -635,17 +730,24 @@ public final class ContentService extends IContentService.Stub {
}
}
- public boolean isSyncActive(Account account, String authority) {
+ public boolean isSyncActive(Account account, String authority, ComponentName cname) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
"no permission to read the sync stats");
int userId = UserHandle.getCallingUserId();
-
+ int callingUid = Binder.getCallingUid();
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
- if (syncManager != null) {
+ if (syncManager == null) {
+ return false;
+ }
+ if (cname == null) {
return syncManager.getSyncStorageEngine().isSyncActive(
- account, userId, authority);
+ new SyncStorageEngine.EndPoint(account, authority, userId));
+ } else if (account == null && authority == null) {
+ verifySignatureForPackage(callingUid, cname.getPackageName(), "isSyncActive");
+ return syncManager.getSyncStorageEngine().isSyncActive(
+ new SyncStorageEngine.EndPoint(cname, userId));
}
} finally {
restoreCallingIdentity(identityToken);
@@ -666,7 +768,7 @@ public final class ContentService extends IContentService.Stub {
}
}
- public SyncStatusInfo getSyncStatus(Account account, String authority) {
+ public SyncStatusInfo getSyncStatus(Account account, String authority, ComponentName cname) {
if (TextUtils.isEmpty(authority)) {
throw new IllegalArgumentException("Authority must not be empty");
}
@@ -674,34 +776,52 @@ public final class ContentService extends IContentService.Stub {
"no permission to read the sync stats");
int userId = UserHandle.getCallingUserId();
+ int callerUid = Binder.getCallingUid();
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
- if (syncManager != null) {
- return syncManager.getSyncStorageEngine().getStatusByAccountAndAuthority(
- account, userId, authority);
+ if (syncManager == null) {
+ return null;
}
+ SyncStorageEngine.EndPoint info;
+ if (cname == null) {
+ info = new SyncStorageEngine.EndPoint(account, authority, userId);
+ } else if (account == null && authority == null) {
+ verifySignatureForPackage(callerUid, cname.getPackageName(), "getSyncStatus");
+ info = new SyncStorageEngine.EndPoint(cname, userId);
+ } else {
+ throw new IllegalArgumentException("Must call sync status with valid authority");
+ }
+ return syncManager.getSyncStorageEngine().getStatusByAuthority(info);
} finally {
restoreCallingIdentity(identityToken);
}
- return null;
}
- public boolean isSyncPending(Account account, String authority) {
+ public boolean isSyncPending(Account account, String authority, ComponentName cname) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
"no permission to read the sync stats");
+ int callerUid = Binder.getCallingUid();
int userId = UserHandle.getCallingUserId();
long identityToken = clearCallingIdentity();
+ SyncManager syncManager = getSyncManager();
+ if (syncManager == null) return false;
+
try {
- SyncManager syncManager = getSyncManager();
- if (syncManager != null) {
- return syncManager.getSyncStorageEngine().isSyncPending(account, userId, authority);
+ SyncStorageEngine.EndPoint info;
+ if (cname == null) {
+ info = new SyncStorageEngine.EndPoint(account, authority, userId);
+ } else if (account == null && authority == null) {
+ verifySignatureForPackage(callerUid, cname.getPackageName(), "isSyncPending");
+ info = new SyncStorageEngine.EndPoint(cname, userId);
+ } else {
+ throw new IllegalArgumentException("Invalid authority specified");
}
+ return syncManager.getSyncStorageEngine().isSyncPending(info);
} finally {
restoreCallingIdentity(identityToken);
}
- return false;
}
public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
@@ -735,6 +855,30 @@ public final class ContentService extends IContentService.Stub {
}
/**
+ * Helper to verify that the provided package name shares the same cert as the caller.
+ * @param callerUid uid of the calling process.
+ * @param packageName package to verify against package of calling application.
+ * @param tag a tag to use when throwing an exception if the signatures don't
+ * match. Cannot be null.
+ * @return true if the calling application and the provided package are signed with the same
+ * certificate.
+ */
+ private boolean verifySignatureForPackage(int callerUid, String packageName, String tag) {
+ PackageManager pm = mContext.getPackageManager();
+ try {
+ int serviceUid = pm.getApplicationInfo(packageName, 0).uid;
+ if (pm.checkSignatures(callerUid, serviceUid) == PackageManager.SIGNATURE_MATCH) {
+ return true;
+ } else {
+ throw new SecurityException(tag + ": Caller certificate does not match that for - "
+ + packageName);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalArgumentException(tag + ": " + packageName + " package not found.");
+ }
+ }
+
+ /**
* Hide this class since it is not part of api,
* but current unittest framework requires it to be public
* @hide
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 71d8d99..e43dea9 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -31,6 +31,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.ISyncAdapter;
import android.content.ISyncContext;
+import android.content.ISyncServiceAdapter;
import android.content.ISyncStatusObserver;
import android.content.Intent;
import android.content.IntentFilter;
@@ -52,6 +53,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
+import android.os.BatteryStats;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -59,6 +61,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -74,6 +77,7 @@ import android.util.Pair;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.accounts.AccountManagerService;
@@ -85,10 +89,8 @@ import com.google.android.collect.Sets;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@@ -99,7 +101,6 @@ import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
-import java.util.concurrent.CountDownLatch;
/**
* @hide
@@ -153,7 +154,7 @@ public class SyncManager {
private static final int INITIALIZATION_UNBIND_DELAY_MS = 5000;
- private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*";
+ private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*/";
private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm";
private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock";
@@ -174,6 +175,7 @@ public class SyncManager {
private final NotificationManager mNotificationMgr;
private AlarmManager mAlarmService = null;
+ private final IBatteryStats mBatteryStats;
private SyncStorageEngine mSyncStorageEngine;
@@ -202,8 +204,9 @@ public class SyncManager {
Log.v(TAG, "Internal storage is low.");
}
mStorageIsLow = true;
- cancelActiveSync(null /* any account */, UserHandle.USER_ALL,
- null /* any authority */);
+ cancelActiveSync(
+ SyncStorageEngine.EndPoint.USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL,
+ null /* any sync */);
} else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Internal storage is ok.");
@@ -221,19 +224,6 @@ public class SyncManager {
}
};
- private BroadcastReceiver mBackgroundDataSettingChanged = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (getConnectivityManager().getBackgroundDataSetting()) {
- scheduleSync(null /* account */, UserHandle.USER_ALL,
- SyncOperation.REASON_BACKGROUND_DATA_SETTINGS_CHANGED,
- null /* authority */,
- new Bundle(), 0 /* delay */, 0 /* delay */,
- false /* onlyThoseWithUnknownSyncableState */);
- }
- }
- };
-
private BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -278,16 +268,16 @@ public class SyncManager {
doDatabaseCleanup();
}
+ AccountAndUser[] accounts = mRunningAccounts;
for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
- if (!containsAccountAndUser(mRunningAccounts,
- currentSyncContext.mSyncOperation.account,
- currentSyncContext.mSyncOperation.userId)) {
+ if (!containsAccountAndUser(accounts,
+ currentSyncContext.mSyncOperation.target.account,
+ currentSyncContext.mSyncOperation.target.userId)) {
Log.d(TAG, "canceling sync since the account is no longer running");
sendSyncFinishedOrCanceledMessage(currentSyncContext,
null /* no result since this is a cancel */);
}
}
-
// we must do this since we don't bother scheduling alarms when
// the accounts are not set yet
sendCheckAlarmsMessage();
@@ -316,9 +306,7 @@ public class SyncManager {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Reconnection detected: clearing all backoffs");
}
- synchronized(mSyncQueue) {
- mSyncStorageEngine.clearAllBackoffsLocked(mSyncQueue);
- }
+ mSyncStorageEngine.clearAllBackoffs(mSyncQueue);
}
sendCheckAlarmsMessage();
}
@@ -384,12 +372,17 @@ public class SyncManager {
mSyncStorageEngine = SyncStorageEngine.getSingleton();
mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() {
@Override
- public void onSyncRequest(Account account, int userId, int reason, String authority,
- Bundle extras) {
- scheduleSync(account, userId, reason, authority, extras,
- 0 /* no delay */,
- 0 /* no delay */,
- false);
+ public void onSyncRequest(SyncStorageEngine.EndPoint info, int reason, Bundle extras) {
+ if (info.target_provider) {
+ scheduleSync(info.account, info.userId, reason, info.provider, extras,
+ 0 /* no flex */,
+ 0 /* run immediately */,
+ false);
+ } else if (info.target_service) {
+ scheduleSync(info.service, info.userId, reason, extras,
+ 0 /* no flex */,
+ 0 /* run immediately */);
+ }
}
});
@@ -421,9 +414,6 @@ public class SyncManager {
context.registerReceiver(mBootCompletedReceiver, intentFilter);
}
- intentFilter = new IntentFilter(ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
- context.registerReceiver(mBackgroundDataSettingChanged, intentFilter);
-
intentFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW);
intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
context.registerReceiver(mStorageIntentReceiver, intentFilter);
@@ -449,6 +439,8 @@ public class SyncManager {
}
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
+ BatteryStats.SERVICE_NAME));
// This WakeLock is used to ensure that we stay awake between the time that we receive
// a sync alarm notification and when we finish processing it. We need to do this
@@ -538,8 +530,75 @@ public class SyncManager {
private void ensureAlarmService() {
if (mAlarmService == null) {
- mAlarmService = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+ mAlarmService = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ }
+ }
+
+ /**
+ * Initiate a sync using the new anonymous service API.
+ * @param cname SyncService component bound to in order to perform the sync.
+ * @param userId the id of the user whose accounts are to be synced. If userId is USER_ALL,
+ * then all users' accounts are considered.
+ * @param uid Linux uid of the application that is performing the sync.
+ * @param extras a Map of SyncAdapter-specific information to control
+ * syncs of a specific provider. Cannot be null.
+ * @param beforeRunTimeMillis milliseconds before <code>runtimeMillis</code> that this sync may
+ * be run.
+ * @param runtimeMillis milliseconds from now by which this sync must be run.
+ */
+ public void scheduleSync(ComponentName cname, int userId, int uid, Bundle extras,
+ long beforeRunTimeMillis, long runtimeMillis) {
+ boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
+ if (isLoggable) {
+ Log.d(TAG, "one off sync for: " + cname + " " + extras.toString());
+ }
+
+ Boolean expedited = extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false);
+ if (expedited) {
+ runtimeMillis = -1; // this means schedule at the front of the queue
+ }
+
+ final boolean ignoreSettings =
+ extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false);
+ int source = SyncStorageEngine.SOURCE_SERVICE;
+ boolean isEnabled = mSyncStorageEngine.getIsTargetServiceActive(cname, userId);
+ // Only schedule this sync if
+ // - we've explicitly been told to ignore settings.
+ // - global sync is enabled for this user.
+ boolean syncAllowed =
+ ignoreSettings
+ || mSyncStorageEngine.getMasterSyncAutomatically(userId);
+ if (!syncAllowed) {
+ if (isLoggable) {
+ Log.d(TAG, "scheduleSync: sync of " + cname + " not allowed, dropping request.");
+ }
+ return;
+ }
+ if (!isEnabled) {
+ if (isLoggable) {
+ Log.d(TAG, "scheduleSync: " + cname + " is not enabled, dropping request");
+ }
+ return;
}
+ SyncStorageEngine.EndPoint info = new SyncStorageEngine.EndPoint(cname, userId);
+ Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(info);
+ long delayUntil = mSyncStorageEngine.getDelayUntilTime(info);
+ final long backoffTime = backoff != null ? backoff.first : 0;
+ if (isLoggable) {
+ Log.v(TAG, "schedule Sync:"
+ + ", delay until " + delayUntil
+ + ", run by " + runtimeMillis
+ + ", flex " + beforeRunTimeMillis
+ + ", source " + source
+ + ", sync service " + cname
+ + ", extras " + extras);
+ }
+ scheduleSyncOperation(
+ new SyncOperation(cname, userId, uid, source, extras,
+ runtimeMillis /* runtime */,
+ beforeRunTimeMillis /* flextime */,
+ backoffTime,
+ delayUntil));
}
/**
@@ -588,9 +647,6 @@ public class SyncManager {
long runtimeMillis, boolean onlyThoseWithUnkownSyncableState) {
boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
- final boolean backgroundDataUsageAllowed = !mBootCompleted ||
- getConnectivityManager().getBackgroundDataSetting();
-
if (extras == null) {
extras = new Bundle();
}
@@ -607,8 +663,6 @@ public class SyncManager {
if (requestedAccount != null && userId != UserHandle.USER_ALL) {
accounts = new AccountAndUser[] { new AccountAndUser(requestedAccount, userId) };
} else {
- // if the accounts aren't configured yet then we can't support an account-less
- // sync request
accounts = mRunningAccounts;
if (accounts.length == 0) {
if (isLoggable) {
@@ -683,12 +737,10 @@ public class SyncManager {
continue;
}
- // always allow if the isSyncable state is unknown
boolean syncAllowed =
- (isSyncable < 0)
+ (isSyncable < 0) // always allow if the isSyncable state is unknown
|| ignoreSettings
- || (backgroundDataUsageAllowed
- && mSyncStorageEngine.getMasterSyncAutomatically(account.userId)
+ || (mSyncStorageEngine.getMasterSyncAutomatically(account.userId)
&& mSyncStorageEngine.getSyncAutomatically(account.account,
account.userId, authority));
if (!syncAllowed) {
@@ -698,11 +750,12 @@ public class SyncManager {
}
continue;
}
-
- Pair<Long, Long> backoff = mSyncStorageEngine
- .getBackoff(account.account, account.userId, authority);
- long delayUntil = mSyncStorageEngine.getDelayUntilTime(account.account,
- account.userId, authority);
+ SyncStorageEngine.EndPoint info =
+ new SyncStorageEngine.EndPoint(
+ account.account, authority, account.userId);
+ Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(info);
+ long delayUntil =
+ mSyncStorageEngine.getDelayUntilTime(info);
final long backoffTime = backoff != null ? backoff.first : 0;
if (isSyncable < 0) {
// Initialisation sync.
@@ -712,6 +765,7 @@ public class SyncManager {
Log.v(TAG, "schedule initialisation Sync:"
+ ", delay until " + delayUntil
+ ", run by " + 0
+ + ", flex " + 0
+ ", source " + source
+ ", account " + account
+ ", authority " + authority
@@ -787,13 +841,12 @@ public class SyncManager {
mSyncHandler.sendMessage(msg);
}
- private void sendCancelSyncsMessage(final Account account, final int userId,
- final String authority) {
+ private void sendCancelSyncsMessage(final SyncStorageEngine.EndPoint info, Bundle extras) {
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CANCEL");
Message msg = mSyncHandler.obtainMessage();
msg.what = SyncHandler.MESSAGE_CANCEL;
- msg.obj = Pair.create(account, authority);
- msg.arg1 = userId;
+ msg.setData(extras);
+ msg.obj = info;
mSyncHandler.sendMessage(msg);
}
@@ -816,10 +869,11 @@ public class SyncManager {
}
private void clearBackoffSetting(SyncOperation op) {
- mSyncStorageEngine.setBackoff(op.account, op.userId, op.authority,
- SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE);
+ mSyncStorageEngine.setBackoff(op.target,
+ SyncStorageEngine.NOT_IN_BACKOFF_MODE,
+ SyncStorageEngine.NOT_IN_BACKOFF_MODE);
synchronized (mSyncQueue) {
- mSyncQueue.onBackoffChanged(op.account, op.userId, op.authority, 0);
+ mSyncQueue.onBackoffChanged(op.target, 0);
}
}
@@ -829,7 +883,7 @@ public class SyncManager {
final long now = SystemClock.elapsedRealtime();
final Pair<Long, Long> previousSettings =
- mSyncStorageEngine.getBackoff(op.account, op.userId, op.authority);
+ mSyncStorageEngine.getBackoff(op.target);
long newDelayInMs = -1;
if (previousSettings != null) {
// don't increase backoff before current backoff is expired. This will happen for op's
@@ -860,14 +914,12 @@ public class SyncManager {
final long backoff = now + newDelayInMs;
- mSyncStorageEngine.setBackoff(op.account, op.userId, op.authority,
- backoff, newDelayInMs);
-
+ mSyncStorageEngine.setBackoff(op.target, backoff, newDelayInMs);
op.backoff = backoff;
op.updateEffectiveRunTime();
synchronized (mSyncQueue) {
- mSyncQueue.onBackoffChanged(op.account, op.userId, op.authority, backoff);
+ mSyncQueue.onBackoffChanged(op.target, backoff);
}
}
@@ -880,20 +932,20 @@ public class SyncManager {
} else {
newDelayUntilTime = 0;
}
- mSyncStorageEngine
- .setDelayUntilTime(op.account, op.userId, op.authority, newDelayUntilTime);
+ mSyncStorageEngine.setDelayUntilTime(op.target, newDelayUntilTime);
synchronized (mSyncQueue) {
- mSyncQueue.onDelayUntilTimeChanged(op.account, op.authority, newDelayUntilTime);
+ mSyncQueue.onDelayUntilTimeChanged(op.target, newDelayUntilTime);
}
}
/**
- * Cancel the active sync if it matches the authority and account.
- * @param account limit the cancelations to syncs with this account, if non-null
- * @param authority limit the cancelations to syncs with this authority, if non-null
+ * Cancel the active sync if it matches the target.
+ * @param info object containing info about which syncs to cancel. The target can
+ * have null account/provider info to specify all accounts/providers.
+ * @param extras if non-null, specifies the exact sync to remove.
*/
- public void cancelActiveSync(Account account, int userId, String authority) {
- sendCancelSyncsMessage(account, userId, authority);
+ public void cancelActiveSync(SyncStorageEngine.EndPoint info, Bundle extras) {
+ sendCancelSyncsMessage(info, extras);
}
/**
@@ -922,24 +974,40 @@ public class SyncManager {
/**
* Remove scheduled sync operations.
- * @param account limit the removals to operations with this account, if non-null
- * @param authority limit the removals to operations with this authority, if non-null
+ * @param info limit the removals to operations that match this target. The target can
+ * have null account/provider info to specify all accounts/providers.
*/
- public void clearScheduledSyncOperations(Account account, int userId, String authority) {
+ public void clearScheduledSyncOperations(SyncStorageEngine.EndPoint info) {
synchronized (mSyncQueue) {
- mSyncQueue.remove(account, userId, authority);
+ mSyncQueue.remove(info, null /* all operations */);
}
- mSyncStorageEngine.setBackoff(account, userId, authority,
+ mSyncStorageEngine.setBackoff(info,
SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE);
}
+ /**
+ * Remove a specified sync, if it exists.
+ * @param info Authority for which the sync is to be removed.
+ * @param extras extras bundle to uniquely identify sync.
+ */
+ public void cancelScheduledSyncOperation(SyncStorageEngine.EndPoint info, Bundle extras) {
+ synchronized (mSyncQueue) {
+ mSyncQueue.remove(info, extras);
+ }
+ // Reset the back-off if there are no more syncs pending.
+ if (!mSyncStorageEngine.isSyncPending(info)) {
+ mSyncStorageEngine.setBackoff(info,
+ SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE);
+ }
+ }
+
void maybeRescheduleSync(SyncResult syncResult, SyncOperation operation) {
boolean isLoggable = Log.isLoggable(TAG, Log.DEBUG);
if (isLoggable) {
Log.d(TAG, "encountered error(s) during the sync: " + syncResult + ", " + operation);
}
- operation = new SyncOperation(operation);
+ operation = new SyncOperation(operation, 0L /* newRunTimeFromNow */);
// The SYNC_EXTRAS_IGNORE_BACKOFF only applies to the first attempt to sync a given
// request. Retries of the request will always honor the backoff, so clear the
@@ -948,25 +1016,29 @@ public class SyncManager {
operation.extras.remove(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF);
}
- // If this sync aborted because the internal sync loop retried too many times then
- // don't reschedule. Otherwise we risk getting into a retry loop.
- // If the operation succeeded to some extent then retry immediately.
- // If this was a two-way sync then retry soft errors with an exponential backoff.
- // If this was an upward sync then schedule a two-way sync immediately.
- // Otherwise do not reschedule.
if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false)) {
- Log.d(TAG, "not retrying sync operation because SYNC_EXTRAS_DO_NOT_RETRY was specified "
- + operation);
+ if (isLoggable) {
+ Log.d(TAG, "not retrying sync operation because SYNC_EXTRAS_DO_NOT_RETRY was specified "
+ + operation);
+ }
} else if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false)
&& !syncResult.syncAlreadyInProgress) {
+ // If this was an upward sync then schedule a two-way sync immediately.
operation.extras.remove(ContentResolver.SYNC_EXTRAS_UPLOAD);
- Log.d(TAG, "retrying sync operation as a two-way sync because an upload-only sync "
- + "encountered an error: " + operation);
+ if (isLoggable) {
+ Log.d(TAG, "retrying sync operation as a two-way sync because an upload-only sync "
+ + "encountered an error: " + operation);
+ }
scheduleSyncOperation(operation);
} else if (syncResult.tooManyRetries) {
- Log.d(TAG, "not retrying sync operation because it retried too many times: "
- + operation);
+ // If this sync aborted because the internal sync loop retried too many times then
+ // don't reschedule. Otherwise we risk getting into a retry loop.
+ if (isLoggable) {
+ Log.d(TAG, "not retrying sync operation because it retried too many times: "
+ + operation);
+ }
} else if (syncResult.madeSomeProgress()) {
+ // If the operation succeeded to some extent then retry immediately.
if (isLoggable) {
Log.d(TAG, "retrying sync operation because even though it had an error "
+ "it achieved some success");
@@ -979,19 +1051,18 @@ public class SyncManager {
}
scheduleSyncOperation(
new SyncOperation(
- operation.account, operation.userId,
- operation.reason,
- operation.syncSource,
- operation.authority, operation.extras,
- DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000, operation.flexTime,
- operation.backoff, operation.delayUntil, operation.allowParallelSyncs));
+ operation,
+ DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000 /* newRunTimeFromNow */)
+ );
} else if (syncResult.hasSoftError()) {
+ // If this was a two-way sync then retry soft errors with an exponential backoff.
if (isLoggable) {
Log.d(TAG, "retrying sync operation because it encountered a soft error: "
+ operation);
}
scheduleSyncOperation(operation);
} else {
+ // Otherwise do not reschedule.
Log.d(TAG, "not retrying sync operation because the error is a hard error: "
+ operation);
}
@@ -1024,9 +1095,12 @@ public class SyncManager {
updateRunningAccounts();
cancelActiveSync(
- null /* any account */,
- userId,
- null /* any authority */);
+ new SyncStorageEngine.EndPoint(
+ null /* any account */,
+ null /* any authority */,
+ userId),
+ null /* any sync. */
+ );
}
private void onUserRemoved(int userId) {
@@ -1035,7 +1109,7 @@ public class SyncManager {
// Clean up the storage engine database
mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId);
synchronized (mSyncQueue) {
- mSyncQueue.removeUser(userId);
+ mSyncQueue.removeUserLocked(userId);
}
}
@@ -1047,6 +1121,7 @@ public class SyncManager {
final SyncOperation mSyncOperation;
final long mHistoryRowId;
ISyncAdapter mSyncAdapter;
+ ISyncServiceAdapter mSyncServiceAdapter;
final long mStartTime;
long mTimeoutStartTime;
boolean mBound;
@@ -1054,6 +1129,7 @@ public class SyncManager {
final int mSyncAdapterUid;
SyncInfo mSyncInfo;
boolean mIsLinkedToDeath = false;
+ String mEventName;
/**
* Create an ActiveSyncContext for an impending sync and grab the wakelock for that
@@ -1072,10 +1148,10 @@ public class SyncManager {
mSyncOperation = syncOperation;
mHistoryRowId = historyRowId;
mSyncAdapter = null;
+ mSyncServiceAdapter = null;
mStartTime = SystemClock.elapsedRealtime();
mTimeoutStartTime = mStartTime;
- mSyncWakeLock = mSyncHandler.getSyncWakeLock(
- mSyncOperation.account, mSyncOperation.authority);
+ mSyncWakeLock = mSyncHandler.getSyncWakeLock(mSyncOperation);
mSyncWakeLock.setWorkSource(new WorkSource(syncAdapterUid));
mSyncWakeLock.acquire();
}
@@ -1102,7 +1178,7 @@ public class SyncManager {
public void onServiceConnected(ComponentName name, IBinder service) {
Message msg = mSyncHandler.obtainMessage();
msg.what = SyncHandler.MESSAGE_SERVICE_CONNECTED;
- msg.obj = new ServiceConnectionData(this, ISyncAdapter.Stub.asInterface(service));
+ msg.obj = new ServiceConnectionData(this, service);
mSyncHandler.sendMessage(msg);
}
@@ -1113,13 +1189,13 @@ public class SyncManager {
mSyncHandler.sendMessage(msg);
}
- boolean bindToSyncAdapter(RegisteredServicesCache.ServiceInfo info, int userId) {
+ boolean bindToSyncAdapter(ComponentName serviceComponent, int userId) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.d(TAG, "bindToSyncAdapter: " + info.componentName + ", connection " + this);
+ Log.d(TAG, "bindToSyncAdapter: " + serviceComponent + ", connection " + this);
}
Intent intent = new Intent();
intent.setAction("android.content.SyncAdapter");
- intent.setComponent(info.componentName);
+ intent.setComponent(serviceComponent);
intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
com.android.internal.R.string.sync_binding_label);
intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(
@@ -1129,9 +1205,16 @@ public class SyncManager {
final boolean bindResult = mContext.bindServiceAsUser(intent, this,
Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
| Context.BIND_ALLOW_OOM_MANAGEMENT,
- new UserHandle(mSyncOperation.userId));
+ new UserHandle(mSyncOperation.target.userId));
if (!bindResult) {
mBound = false;
+ } else {
+ try {
+ mEventName = mSyncOperation.wakeLockName();
+ mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_SYNC_START,
+ mEventName, mSyncAdapterUid);
+ } catch (RemoteException e) {
+ }
}
return bindResult;
}
@@ -1147,12 +1230,16 @@ public class SyncManager {
if (mBound) {
mBound = false;
mContext.unbindService(this);
+ try {
+ mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_SYNC_FINISH,
+ mEventName, mSyncAdapterUid);
+ } catch (RemoteException e) {
+ }
}
mSyncWakeLock.release();
mSyncWakeLock.setWorkSource(null);
}
- @Override
public String toString() {
StringBuilder sb = new StringBuilder();
toString(sb);
@@ -1291,11 +1378,13 @@ public class SyncManager {
int row = table.getNumRows();
Pair<AuthorityInfo, SyncStatusInfo> syncAuthoritySyncStatus =
mSyncStorageEngine.getCopyOfAuthorityWithSyncStatus(
- account.account, account.userId, syncAdapterType.type.authority);
+ new SyncStorageEngine.EndPoint(
+ account.account,
+ syncAdapterType.type.authority,
+ account.userId));
SyncStorageEngine.AuthorityInfo settings = syncAuthoritySyncStatus.first;
SyncStatusInfo status = syncAuthoritySyncStatus.second;
-
- String authority = settings.authority;
+ String authority = settings.target.provider;
if (authority.length() > 50) {
authority = authority.substring(authority.length() - 50);
}
@@ -1416,14 +1505,25 @@ public class SyncManager {
int maxAuthority = 0;
int maxAccount = 0;
for (SyncStorageEngine.SyncHistoryItem item : items) {
- SyncStorageEngine.AuthorityInfo authority
+ SyncStorageEngine.AuthorityInfo authorityInfo
= mSyncStorageEngine.getAuthority(item.authorityId);
final String authorityName;
final String accountKey;
- if (authority != null) {
- authorityName = authority.authority;
- accountKey = authority.account.name + "/" + authority.account.type
- + " u" + authority.userId;
+ if (authorityInfo != null) {
+ if (authorityInfo.target.target_provider) {
+ authorityName = authorityInfo.target.provider;
+ accountKey = authorityInfo.target.account.name + "/"
+ + authorityInfo.target.account.type
+ + " u" + authorityInfo.target.userId;
+ } else if (authorityInfo.target.target_service) {
+ authorityName = authorityInfo.target.service.getPackageName() + "/"
+ + authorityInfo.target.service.getClassName()
+ + " u" + authorityInfo.target.userId;
+ accountKey = "no account";
+ } else {
+ authorityName = "Unknown";
+ accountKey = "Unknown";
+ }
} else {
authorityName = "Unknown";
accountKey = "Unknown";
@@ -1544,14 +1644,25 @@ public class SyncManager {
final PackageManager pm = mContext.getPackageManager();
for (int i = 0; i < N; i++) {
SyncStorageEngine.SyncHistoryItem item = items.get(i);
- SyncStorageEngine.AuthorityInfo authority
+ SyncStorageEngine.AuthorityInfo authorityInfo
= mSyncStorageEngine.getAuthority(item.authorityId);
final String authorityName;
final String accountKey;
- if (authority != null) {
- authorityName = authority.authority;
- accountKey = authority.account.name + "/" + authority.account.type
- + " u" + authority.userId;
+ if (authorityInfo != null) {
+ if (authorityInfo.target.target_provider) {
+ authorityName = authorityInfo.target.provider;
+ accountKey = authorityInfo.target.account.name + "/"
+ + authorityInfo.target.account.type
+ + " u" + authorityInfo.target.userId;
+ } else if (authorityInfo.target.target_service) {
+ authorityName = authorityInfo.target.service.getPackageName() + "/"
+ + authorityInfo.target.service.getClassName()
+ + " u" + authorityInfo.target.userId;
+ accountKey = "none";
+ } else {
+ authorityName = "Unknown";
+ accountKey = "Unknown";
+ }
} else {
authorityName = "Unknown";
accountKey = "Unknown";
@@ -1610,14 +1721,25 @@ public class SyncManager {
if (extras == null || extras.size() == 0) {
continue;
}
- final SyncStorageEngine.AuthorityInfo authority
+ final SyncStorageEngine.AuthorityInfo authorityInfo
= mSyncStorageEngine.getAuthority(item.authorityId);
final String authorityName;
final String accountKey;
- if (authority != null) {
- authorityName = authority.authority;
- accountKey = authority.account.name + "/" + authority.account.type
- + " u" + authority.userId;
+ if (authorityInfo != null) {
+ if (authorityInfo.target.target_provider) {
+ authorityName = authorityInfo.target.provider;
+ accountKey = authorityInfo.target.account.name + "/"
+ + authorityInfo.target.account.type
+ + " u" + authorityInfo.target.userId;
+ } else if (authorityInfo.target.target_service) {
+ authorityName = authorityInfo.target.service.getPackageName() + "/"
+ + authorityInfo.target.service.getClassName()
+ + " u" + authorityInfo.target.userId;
+ accountKey = "none";
+ } else {
+ authorityName = "Unknown";
+ accountKey = "Unknown";
+ }
} else {
authorityName = "Unknown";
accountKey = "Unknown";
@@ -1761,10 +1883,11 @@ public class SyncManager {
class ServiceConnectionData {
public final ActiveSyncContext activeSyncContext;
- public final ISyncAdapter syncAdapter;
- ServiceConnectionData(ActiveSyncContext activeSyncContext, ISyncAdapter syncAdapter) {
+ public final IBinder adapter;
+
+ ServiceConnectionData(ActiveSyncContext activeSyncContext, IBinder adapter) {
this.activeSyncContext = activeSyncContext;
- this.syncAdapter = syncAdapter;
+ this.adapter = adapter;
}
}
@@ -1784,11 +1907,11 @@ public class SyncManager {
public final SyncNotificationInfo mSyncNotificationInfo = new SyncNotificationInfo();
private Long mAlarmScheduleTime = null;
public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker();
- private final HashMap<Pair<Account, String>, PowerManager.WakeLock> mWakeLocks =
- Maps.newHashMap();
+ private final HashMap<String, PowerManager.WakeLock> mWakeLocks = Maps.newHashMap();
+
private List<Message> mBootQueue = new ArrayList<Message>();
- public void onBootCompleted() {
+ public void onBootCompleted() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Boot completed, clearing boot queue.");
}
@@ -1803,12 +1926,11 @@ public class SyncManager {
}
}
- private PowerManager.WakeLock getSyncWakeLock(Account account, String authority) {
- final Pair<Account, String> wakeLockKey = Pair.create(account, authority);
+ private PowerManager.WakeLock getSyncWakeLock(SyncOperation operation) {
+ final String wakeLockKey = operation.wakeLockName();
PowerManager.WakeLock wakeLock = mWakeLocks.get(wakeLockKey);
if (wakeLock == null) {
- final String name = SYNC_WAKE_LOCK_PREFIX + "/" + authority + "/" + account.type
- + "/" + account.name;
+ final String name = SYNC_WAKE_LOCK_PREFIX + wakeLockKey;
wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name);
wakeLock.setReferenceCounted(false);
mWakeLocks.put(wakeLockKey, wakeLock);
@@ -1861,7 +1983,6 @@ public class SyncManager {
super(looper);
}
- @Override
public void handleMessage(Message msg) {
if (tryEnqueueMessageUntilReadyToRun(msg)) {
return;
@@ -1881,12 +2002,13 @@ public class SyncManager {
earliestFuturePollTime = scheduleReadyPeriodicSyncs();
switch (msg.what) {
case SyncHandler.MESSAGE_CANCEL: {
- Pair<Account, String> payload = (Pair<Account, String>) msg.obj;
+ SyncStorageEngine.EndPoint payload = (SyncStorageEngine.EndPoint) msg.obj;
+ Bundle extras = msg.peekData();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CANCEL: "
- + payload.first + ", " + payload.second);
+ + payload + " bundle: " + extras);
}
- cancelActiveSyncLocked(payload.first, msg.arg1, payload.second);
+ cancelActiveSyncLocked(payload, extras);
nextPendingSyncTime = maybeStartNextSyncLocked();
break;
}
@@ -1895,35 +2017,38 @@ public class SyncManager {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_FINISHED");
}
- SyncHandlerMessagePayload payload = (SyncHandlerMessagePayload)msg.obj;
+ SyncHandlerMessagePayload payload = (SyncHandlerMessagePayload) msg.obj;
if (!isSyncStillActive(payload.activeSyncContext)) {
Log.d(TAG, "handleSyncHandlerMessage: dropping since the "
+ "sync is no longer active: "
+ payload.activeSyncContext);
break;
}
- runSyncFinishedOrCanceledLocked(payload.syncResult, payload.activeSyncContext);
+ runSyncFinishedOrCanceledLocked(payload.syncResult,
+ payload.activeSyncContext);
// since a sync just finished check if it is time to start a new sync
nextPendingSyncTime = maybeStartNextSyncLocked();
break;
case SyncHandler.MESSAGE_SERVICE_CONNECTED: {
- ServiceConnectionData msgData = (ServiceConnectionData)msg.obj;
+ ServiceConnectionData msgData = (ServiceConnectionData) msg.obj;
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: "
+ msgData.activeSyncContext);
}
// check that this isn't an old message
if (isSyncStillActive(msgData.activeSyncContext)) {
- runBoundToSyncAdapter(msgData.activeSyncContext, msgData.syncAdapter);
+ runBoundToAdapter(
+ msgData.activeSyncContext,
+ msgData.adapter);
}
break;
}
case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: {
final ActiveSyncContext currentSyncContext =
- ((ServiceConnectionData)msg.obj).activeSyncContext;
+ ((ServiceConnectionData) msg.obj).activeSyncContext;
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: "
+ currentSyncContext);
@@ -1932,12 +2057,15 @@ public class SyncManager {
if (isSyncStillActive(currentSyncContext)) {
// cancel the sync if we have a syncadapter, which means one is
// outstanding
- if (currentSyncContext.mSyncAdapter != null) {
- try {
+ try {
+ if (currentSyncContext.mSyncAdapter != null) {
currentSyncContext.mSyncAdapter.cancelSync(currentSyncContext);
- } catch (RemoteException e) {
- // we don't need to retry this in this case
+ } else if (currentSyncContext.mSyncServiceAdapter != null) {
+ currentSyncContext.mSyncServiceAdapter
+ .cancelSync(currentSyncContext);
}
+ } catch (RemoteException e) {
+ // We don't need to retry this in this case.
}
// pretend that the sync failed with an IOException,
@@ -1982,9 +2110,46 @@ public class SyncManager {
}
}
+ private boolean isDispatchable(SyncStorageEngine.EndPoint target) {
+ final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
+ if (target.target_provider) {
+ // skip the sync if the account of this operation no longer exists
+ AccountAndUser[] accounts = mRunningAccounts;
+ if (!containsAccountAndUser(
+ accounts, target.account, target.userId)) {
+ return false;
+ }
+ if (!mSyncStorageEngine.getMasterSyncAutomatically(target.userId)
+ || !mSyncStorageEngine.getSyncAutomatically(
+ target.account,
+ target.userId,
+ target.provider)) {
+ if (isLoggable) {
+ Log.v(TAG, " Not scheduling periodic operation: sync turned off.");
+ }
+ return false;
+ }
+ if (getIsSyncable(target.account, target.userId, target.provider)
+ == 0) {
+ if (isLoggable) {
+ Log.v(TAG, " Not scheduling periodic operation: isSyncable == 0.");
+ }
+ return false;
+ }
+ } else if (target.target_service) {
+ if (mSyncStorageEngine.getIsTargetServiceActive(target.service, target.userId)) {
+ if (isLoggable) {
+ Log.v(TAG, " Not scheduling periodic operation: isEnabled == 0.");
+ }
+ return false;
+ }
+ }
+ return true;
+ }
+
/**
* Turn any periodic sync operations that are ready to run into pending sync operations.
- * @return the desired start time of the earliest future periodic sync operation,
+ * @return the desired start time of the earliest future periodic sync operation,
* in milliseconds since boot
*/
private long scheduleReadyPeriodicSyncs() {
@@ -1992,14 +2157,7 @@ public class SyncManager {
if (isLoggable) {
Log.v(TAG, "scheduleReadyPeriodicSyncs");
}
- final boolean backgroundDataUsageAllowed =
- getConnectivityManager().getBackgroundDataSetting();
long earliestFuturePollTime = Long.MAX_VALUE;
- if (!backgroundDataUsageAllowed) {
- return earliestFuturePollTime;
- }
-
- AccountAndUser[] accounts = mRunningAccounts;
final long nowAbsolute = System.currentTimeMillis();
final long shiftedNowAbsolute = (0 < nowAbsolute - mSyncRandomOffsetMillis)
@@ -2010,40 +2168,31 @@ public class SyncManager {
for (Pair<AuthorityInfo, SyncStatusInfo> info : infos) {
final AuthorityInfo authorityInfo = info.first;
final SyncStatusInfo status = info.second;
- if (TextUtils.isEmpty(authorityInfo.authority)) {
- Log.e(TAG, "Got an empty provider string. Skipping: " + authorityInfo);
- continue;
- }
- // skip the sync if the account of this operation no longer exists
- if (!containsAccountAndUser(
- accounts, authorityInfo.account, authorityInfo.userId)) {
- continue;
- }
- if (!mSyncStorageEngine.getMasterSyncAutomatically(authorityInfo.userId)
- || !mSyncStorageEngine.getSyncAutomatically(
- authorityInfo.account, authorityInfo.userId,
- authorityInfo.authority)) {
+ if (TextUtils.isEmpty(authorityInfo.target.provider)) {
+ Log.e(TAG, "Got an empty provider string. Skipping: "
+ + authorityInfo.target.provider);
continue;
}
- if (getIsSyncable(
- authorityInfo.account, authorityInfo.userId, authorityInfo.authority)
- == 0) {
+ if (!isDispatchable(authorityInfo.target)) {
continue;
}
for (int i = 0, N = authorityInfo.periodicSyncs.size(); i < N; i++) {
final PeriodicSync sync = authorityInfo.periodicSyncs.get(i);
final Bundle extras = sync.extras;
- final long periodInMillis = sync.period * 1000;
- final long flexInMillis = sync.flexTime * 1000;
+ final Long periodInMillis = sync.period * 1000;
+ final Long flexInMillis = sync.flexTime * 1000;
// Skip if the period is invalid.
if (periodInMillis <= 0) {
continue;
}
// Find when this periodic sync was last scheduled to run.
final long lastPollTimeAbsolute = status.getPeriodicSyncTime(i);
+ final long shiftedLastPollTimeAbsolute =
+ (0 < lastPollTimeAbsolute - mSyncRandomOffsetMillis) ?
+ (lastPollTimeAbsolute - mSyncRandomOffsetMillis) : 0;
long remainingMillis
= periodInMillis - (shiftedNowAbsolute % periodInMillis);
long timeSinceLastRunMillis
@@ -2054,12 +2203,13 @@ public class SyncManager {
boolean runEarly = remainingMillis <= flexInMillis
&& timeSinceLastRunMillis > periodInMillis - flexInMillis;
if (isLoggable) {
- Log.v(TAG, "sync: " + i + " for " + authorityInfo.authority + "."
+ Log.v(TAG, "sync: " + i + " for " + authorityInfo.target + "."
+ " period: " + (periodInMillis)
+ " flex: " + (flexInMillis)
+ " remaining: " + (remainingMillis)
+ " time_since_last: " + timeSinceLastRunMillis
+ " last poll absol: " + lastPollTimeAbsolute
+ + " last poll shifed: " + shiftedLastPollTimeAbsolute
+ " shifted now: " + shiftedNowAbsolute
+ " run_early: " + runEarly);
}
@@ -2073,41 +2223,49 @@ public class SyncManager {
* future, sync now and reinitialize. This can happen for
* example if the user changed the time, synced and changed
* back.
- * Case 3: If we failed to sync at the last scheduled
- * time.
+ * Case 3: If we failed to sync at the last scheduled time.
* Case 4: This sync is close enough to the time that we can schedule it.
*/
- if (runEarly // Case 4
- || remainingMillis == periodInMillis // Case 1
+ if (remainingMillis == periodInMillis // Case 1
|| lastPollTimeAbsolute > nowAbsolute // Case 2
- || timeSinceLastRunMillis >= periodInMillis) { // Case 3
+ || timeSinceLastRunMillis >= periodInMillis // Case 3
+ || runEarly) { // Case 4
// Sync now
-
- final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(
- authorityInfo.account, authorityInfo.userId,
- authorityInfo.authority);
- final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
- syncAdapterInfo = mSyncAdapters.getServiceInfo(
- SyncAdapterType.newKey(
- authorityInfo.authority, authorityInfo.account.type),
- authorityInfo.userId);
- if (syncAdapterInfo == null) {
- continue;
- }
+ SyncStorageEngine.EndPoint target = authorityInfo.target;
+ final Pair<Long, Long> backoff =
+ mSyncStorageEngine.getBackoff(target);
mSyncStorageEngine.setPeriodicSyncTime(authorityInfo.ident,
authorityInfo.periodicSyncs.get(i), nowAbsolute);
- scheduleSyncOperation(
- new SyncOperation(authorityInfo.account, authorityInfo.userId,
- SyncOperation.REASON_PERIODIC,
- SyncStorageEngine.SOURCE_PERIODIC,
- authorityInfo.authority, extras,
- 0 /* runtime */, 0 /* flex */,
- backoff != null ? backoff.first : 0,
- mSyncStorageEngine.getDelayUntilTime(
- authorityInfo.account, authorityInfo.userId,
- authorityInfo.authority),
- syncAdapterInfo.type.allowParallelSyncs()));
-
+
+ if (target.target_provider) {
+ final RegisteredServicesCache.ServiceInfo<SyncAdapterType>
+ syncAdapterInfo = mSyncAdapters.getServiceInfo(
+ SyncAdapterType.newKey(
+ target.provider, target.account.type),
+ target.userId);
+ if (syncAdapterInfo == null) {
+ continue;
+ }
+ scheduleSyncOperation(
+ new SyncOperation(target.account, target.userId,
+ SyncOperation.REASON_PERIODIC,
+ SyncStorageEngine.SOURCE_PERIODIC,
+ target.provider, extras,
+ 0 /* runtime */, 0 /* flex */,
+ backoff != null ? backoff.first : 0,
+ mSyncStorageEngine.getDelayUntilTime(target),
+ syncAdapterInfo.type.allowParallelSyncs()));
+ } else if (target.target_service) {
+ scheduleSyncOperation(
+ new SyncOperation(target.service, target.userId,
+ SyncOperation.REASON_PERIODIC,
+ SyncStorageEngine.SOURCE_PERIODIC,
+ extras,
+ 0 /* runtime */,
+ 0 /* flex */,
+ backoff != null ? backoff.first : 0,
+ mSyncStorageEngine.getDelayUntilTime(target)));
+ }
}
// Compute when this periodic sync should next run.
long nextPollTimeAbsolute;
@@ -2154,8 +2312,7 @@ public class SyncManager {
// If the accounts aren't known yet then we aren't ready to run. We will be kicked
// when the account lookup request does complete.
- AccountAndUser[] accounts = mRunningAccounts;
- if (accounts == INITIAL_ACCOUNTS_ARRAY) {
+ if (mRunningAccounts == INITIAL_ACCOUNTS_ARRAY) {
if (isLoggable) {
Log.v(TAG, "maybeStartNextSync: accounts not known, skipping");
}
@@ -2165,9 +2322,6 @@ public class SyncManager {
// Otherwise consume SyncOperations from the head of the SyncQueue until one is
// found that is runnable (not disabled, etc). If that one is ready to run then
// start it, otherwise just get out.
- final boolean backgroundDataUsageAllowed =
- getConnectivityManager().getBackgroundDataSetting();
-
final long now = SystemClock.elapsedRealtime();
// will be set to the next time that a sync should be considered for running
@@ -2189,40 +2343,23 @@ public class SyncManager {
while (operationIterator.hasNext()) {
final SyncOperation op = operationIterator.next();
- // Drop the sync if the account of this operation no longer exists.
- if (!containsAccountAndUser(accounts, op.account, op.userId)) {
- operationIterator.remove();
- mSyncStorageEngine.deleteFromPending(op.pendingOperation);
+ // If the user is not running, skip the request.
+ if (!activityManager.isUserRunning(op.target.userId)) {
+ final UserInfo userInfo = mUserManager.getUserInfo(op.target.userId);
+ if (userInfo == null) {
+ removedUsers.add(op.target.userId);
+ }
if (isLoggable) {
- Log.v(TAG, " Dropping sync operation: account doesn't exist.");
+ Log.v(TAG, " Dropping all sync operations for + "
+ + op.target.userId + ": user not running.");
}
continue;
}
-
- // Drop this sync request if it isn't syncable.
- int syncableState = getIsSyncable(
- op.account, op.userId, op.authority);
- if (syncableState == 0) {
+ if (!isOperationValidLocked(op)) {
operationIterator.remove();
mSyncStorageEngine.deleteFromPending(op.pendingOperation);
- if (isLoggable) {
- Log.v(TAG, " Dropping sync operation: isSyncable == 0.");
- }
continue;
}
-
- // If the user is not running, drop the request.
- if (!activityManager.isUserRunning(op.userId)) {
- final UserInfo userInfo = mUserManager.getUserInfo(op.userId);
- if (userInfo == null) {
- removedUsers.add(op.userId);
- }
- if (isLoggable) {
- Log.v(TAG, " Dropping sync operation: user not running.");
- }
- continue;
- }
-
// If the next run time is in the future, even given the flexible scheduling,
// return the time.
if (op.effectiveRunTime - op.flexTime > now) {
@@ -2230,51 +2367,16 @@ public class SyncManager {
nextReadyToRunTime = op.effectiveRunTime;
}
if (isLoggable) {
- Log.v(TAG, " Dropping sync operation: Sync too far in future.");
+ Log.v(TAG, " Not running sync operation: Sync too far in future."
+ + "effective: " + op.effectiveRunTime + " flex: " + op.flexTime
+ + " now: " + now);
}
continue;
}
-
- // If the op isn't allowed on metered networks and we're on one, drop it.
- if (getConnectivityManager().isActiveNetworkMetered()
- && op.isMeteredDisallowed()) {
- operationIterator.remove();
- mSyncStorageEngine.deleteFromPending(op.pendingOperation);
- continue;
- }
-
- // TODO: change this behaviour for non-registered syncs.
- final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
- syncAdapterInfo = mSyncAdapters.getServiceInfo(
- SyncAdapterType.newKey(op.authority, op.account.type), op.userId);
-
- // only proceed if network is connected for requesting UID
- final boolean uidNetworkConnected;
- if (syncAdapterInfo != null) {
- final NetworkInfo networkInfo = getConnectivityManager()
- .getActiveNetworkInfoForUid(syncAdapterInfo.uid);
- uidNetworkConnected = networkInfo != null && networkInfo.isConnected();
- } else {
- uidNetworkConnected = false;
- }
-
- // skip the sync if it isn't manual, and auto sync or
- // background data usage is disabled or network is
- // disconnected for the target UID.
- if (!op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false)
- && (syncableState > 0)
- && (!mSyncStorageEngine.getMasterSyncAutomatically(op.userId)
- || !backgroundDataUsageAllowed
- || !uidNetworkConnected
- || !mSyncStorageEngine.getSyncAutomatically(
- op.account, op.userId, op.authority))) {
- operationIterator.remove();
- mSyncStorageEngine.deleteFromPending(op.pendingOperation);
- continue;
- }
-
+ // Add this sync to be run.
operations.add(op);
}
+
for (Integer user : removedUsers) {
// if it's still removed
if (mUserManager.getUserInfo(user) == null) {
@@ -2316,13 +2418,9 @@ public class SyncManager {
}
}
}
- if (activeOp.account.type.equals(candidate.account.type)
- && activeOp.authority.equals(candidate.authority)
- && activeOp.userId == candidate.userId
- && (!activeOp.allowParallelSyncs
- || activeOp.account.name.equals(candidate.account.name))) {
+ if (activeOp.isConflict(candidate)) {
conflict = activeSyncContext;
- // don't break out since we want to do a full count of the varieties
+ // don't break out since we want to do a full count of the varieties.
} else {
if (candidateIsInitialization == activeOp.isInitialization()
&& activeSyncContext.mStartTime + MAX_TIME_PER_SYNC < now) {
@@ -2372,8 +2470,8 @@ public class SyncManager {
// is null. Reschedule the active sync and start the candidate.
toReschedule = oldestNonExpeditedRegular;
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "canceling and rescheduling sync since an expedited is ready to run, "
- + oldestNonExpeditedRegular);
+ Log.v(TAG, "canceling and rescheduling sync since an expedited is ready to"
+ + " run, " + oldestNonExpeditedRegular);
}
} else if (longRunning != null
&& (candidateIsInitialization
@@ -2402,7 +2500,114 @@ public class SyncManager {
}
return nextReadyToRunTime;
- }
+ }
+
+ /**
+ * Determine if a sync is no longer valid and should be dropped from the sync queue and its
+ * pending op deleted.
+ * @param op operation for which the sync is to be scheduled.
+ */
+ private boolean isOperationValidLocked(SyncOperation op) {
+ final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
+ int targetUid;
+ int state;
+ final SyncStorageEngine.EndPoint target = op.target;
+ boolean syncEnabled = mSyncStorageEngine.getMasterSyncAutomatically(target.userId);
+ if (target.target_provider) {
+ // Drop the sync if the account of this operation no longer exists.
+ AccountAndUser[] accounts = mRunningAccounts;
+ if (!containsAccountAndUser(accounts, target.account, target.userId)) {
+ if (isLoggable) {
+ Log.v(TAG, " Dropping sync operation: account doesn't exist.");
+ }
+ return false;
+ }
+ // Drop this sync request if it isn't syncable.
+ state = getIsSyncable(target.account, target.userId, target.provider);
+ if (state == 0) {
+ if (isLoggable) {
+ Log.v(TAG, " Dropping sync operation: isSyncable == 0.");
+ }
+ return false;
+ }
+ syncEnabled = syncEnabled && mSyncStorageEngine.getSyncAutomatically(
+ target.account, target.userId, target.provider);
+
+ final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
+ syncAdapterInfo = mSyncAdapters.getServiceInfo(
+ SyncAdapterType.newKey(
+ target.provider, target.account.type), target.userId);
+ if (syncAdapterInfo != null) {
+ targetUid = syncAdapterInfo.uid;
+ } else {
+ if (isLoggable) {
+ Log.v(TAG, " Dropping sync operation: No sync adapter registered"
+ + "for: " + target);
+ }
+ return false;
+ }
+ } else if (target.target_service) {
+ state = mSyncStorageEngine.getIsTargetServiceActive(target.service, target.userId)
+ ? 1 : 0;
+ if (state == 0) {
+ // TODO: Change this to not drop disabled syncs - keep them in the pending queue.
+ if (isLoggable) {
+ Log.v(TAG, " Dropping sync operation: isActive == 0.");
+ }
+ return false;
+ }
+ try {
+ targetUid = mContext.getPackageManager()
+ .getServiceInfo(target.service, 0)
+ .applicationInfo
+ .uid;
+ } catch (PackageManager.NameNotFoundException e) {
+ if (isLoggable) {
+ Log.v(TAG, " Dropping sync operation: No service registered for: "
+ + target.service);
+ }
+ return false;
+ }
+ } else {
+ Log.e(TAG, "Unknown target for Sync Op: " + target);
+ return false;
+ }
+
+ // We ignore system settings that specify the sync is invalid if:
+ // 1) It's manual - we try it anyway. When/if it fails it will be rescheduled.
+ // or
+ // 2) it's an initialisation sync - we just need to connect to it.
+ final boolean ignoreSystemConfiguration =
+ op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false)
+ || (state < 0);
+
+ // Sync not enabled.
+ if (!syncEnabled && !ignoreSystemConfiguration) {
+ if (isLoggable) {
+ Log.v(TAG, " Dropping sync operation: disallowed by settings/network.");
+ }
+ return false;
+ }
+ // Network down.
+ final NetworkInfo networkInfo = getConnectivityManager()
+ .getActiveNetworkInfoForUid(targetUid);
+ final boolean uidNetworkConnected = networkInfo != null && networkInfo.isConnected();
+ if (!uidNetworkConnected && !ignoreSystemConfiguration) {
+ if (isLoggable) {
+ Log.v(TAG, " Dropping sync operation: disallowed by settings/network.");
+ }
+ return false;
+ }
+ // Metered network.
+ if (op.isNotAllowedOnMetered() && getConnectivityManager().isActiveNetworkMetered()
+ && !ignoreSystemConfiguration) {
+ if (isLoggable) {
+ Log.v(TAG, " Dropping sync operation: not allowed on metered network.");
+ }
+ return false;
+ }
+ return true;
+ }
private boolean dispatchSyncOperation(SyncOperation op) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -2412,27 +2617,48 @@ public class SyncManager {
Log.v(TAG, syncContext.toString());
}
}
-
- // connect to the sync adapter
- SyncAdapterType syncAdapterType = SyncAdapterType.newKey(op.authority, op.account.type);
- final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
- syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, op.userId);
- if (syncAdapterInfo == null) {
- Log.d(TAG, "can't find a sync adapter for " + syncAdapterType
- + ", removing settings for it");
- mSyncStorageEngine.removeAuthority(op.account, op.userId, op.authority);
- return false;
+ // Connect to the sync adapter.
+ int targetUid;
+ ComponentName targetComponent;
+ final SyncStorageEngine.EndPoint info = op.target;
+ if (info.target_provider) {
+ SyncAdapterType syncAdapterType =
+ SyncAdapterType.newKey(info.provider, info.account.type);
+ final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
+ syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, info.userId);
+ if (syncAdapterInfo == null) {
+ Log.d(TAG, "can't find a sync adapter for " + syncAdapterType
+ + ", removing settings for it");
+ mSyncStorageEngine.removeAuthority(info);
+ return false;
+ }
+ targetUid = syncAdapterInfo.uid;
+ targetComponent = syncAdapterInfo.componentName;
+ } else {
+ // TODO: Store the uid of the service as part of the authority info in order to
+ // avoid this call?
+ try {
+ targetUid = mContext.getPackageManager()
+ .getServiceInfo(info.service, 0)
+ .applicationInfo
+ .uid;
+ targetComponent = info.service;
+ } catch(PackageManager.NameNotFoundException e) {
+ Log.d(TAG, "Can't find a service for " + info.service
+ + ", removing settings for it");
+ mSyncStorageEngine.removeAuthority(info);
+ return false;
+ }
}
-
ActiveSyncContext activeSyncContext =
- new ActiveSyncContext(op, insertStartSyncEvent(op), syncAdapterInfo.uid);
+ new ActiveSyncContext(op, insertStartSyncEvent(op), targetUid);
activeSyncContext.mSyncInfo = mSyncStorageEngine.addActiveSync(activeSyncContext);
mActiveSyncContexts.add(activeSyncContext);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "dispatchSyncOperation: starting " + activeSyncContext);
}
- if (!activeSyncContext.bindToSyncAdapter(syncAdapterInfo, op.userId)) {
- Log.e(TAG, "Bind attempt failed to " + syncAdapterInfo);
+ if (!activeSyncContext.bindToSyncAdapter(targetComponent, info.userId)) {
+ Log.e(TAG, "Bind attempt failed - target: " + targetComponent);
closeActiveSyncContext(activeSyncContext);
return false;
}
@@ -2440,47 +2666,54 @@ public class SyncManager {
return true;
}
- private void runBoundToSyncAdapter(final ActiveSyncContext activeSyncContext,
- ISyncAdapter syncAdapter) {
- activeSyncContext.mSyncAdapter = syncAdapter;
+ private void runBoundToAdapter(final ActiveSyncContext activeSyncContext,
+ IBinder syncAdapter) {
final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
try {
activeSyncContext.mIsLinkedToDeath = true;
- syncAdapter.asBinder().linkToDeath(activeSyncContext, 0);
-
- syncAdapter.startSync(activeSyncContext, syncOperation.authority,
- syncOperation.account, syncOperation.extras);
+ syncAdapter.linkToDeath(activeSyncContext, 0);
+
+ if (syncOperation.target.target_provider) {
+ activeSyncContext.mSyncAdapter = ISyncAdapter.Stub.asInterface(syncAdapter);
+ activeSyncContext.mSyncAdapter
+ .startSync(activeSyncContext, syncOperation.target.provider,
+ syncOperation.target.account, syncOperation.extras);
+ } else if (syncOperation.target.target_service) {
+ activeSyncContext.mSyncServiceAdapter =
+ ISyncServiceAdapter.Stub.asInterface(syncAdapter);
+ activeSyncContext.mSyncServiceAdapter
+ .startSync(activeSyncContext, syncOperation.extras);
+ }
} catch (RemoteException remoteExc) {
Log.d(TAG, "maybeStartNextSync: caught a RemoteException, rescheduling", remoteExc);
closeActiveSyncContext(activeSyncContext);
increaseBackoffSetting(syncOperation);
- scheduleSyncOperation(new SyncOperation(syncOperation));
+ scheduleSyncOperation(
+ new SyncOperation(syncOperation, 0L /* newRunTimeFromNow */));
} catch (RuntimeException exc) {
closeActiveSyncContext(activeSyncContext);
Log.e(TAG, "Caught RuntimeException while starting the sync " + syncOperation, exc);
}
}
- private void cancelActiveSyncLocked(Account account, int userId, String authority) {
+ /**
+ * Cancel the sync for the provided target that matches the given bundle.
+ * @param info can have null fields to indicate all the active syncs for that field.
+ */
+ private void cancelActiveSyncLocked(SyncStorageEngine.EndPoint info, Bundle extras) {
ArrayList<ActiveSyncContext> activeSyncs =
new ArrayList<ActiveSyncContext>(mActiveSyncContexts);
for (ActiveSyncContext activeSyncContext : activeSyncs) {
if (activeSyncContext != null) {
- // if an account was specified then only cancel the sync if it matches
- if (account != null) {
- if (!account.equals(activeSyncContext.mSyncOperation.account)) {
- continue;
- }
- }
- // if an authority was specified then only cancel the sync if it matches
- if (authority != null) {
- if (!authority.equals(activeSyncContext.mSyncOperation.authority)) {
- continue;
- }
+ final SyncStorageEngine.EndPoint opInfo =
+ activeSyncContext.mSyncOperation.target;
+ if (!opInfo.matchesSpec(info)) {
+ continue;
}
- // check if the userid matches
- if (userId != UserHandle.USER_ALL
- && userId != activeSyncContext.mSyncOperation.userId) {
+ if (extras != null &&
+ !syncExtrasEquals(activeSyncContext.mSyncOperation.extras,
+ extras,
+ false /* no config settings */)) {
continue;
}
runSyncFinishedOrCanceledLocked(null /* no result since this is a cancel */,
@@ -2493,16 +2726,20 @@ public class SyncManager {
ActiveSyncContext activeSyncContext) {
boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
+ final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
+ final SyncStorageEngine.EndPoint info = syncOperation.target;
+
if (activeSyncContext.mIsLinkedToDeath) {
- activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0);
+ if (info.target_provider) {
+ activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0);
+ } else {
+ activeSyncContext.mSyncServiceAdapter.asBinder()
+ .unlinkToDeath(activeSyncContext, 0);
+ }
activeSyncContext.mIsLinkedToDeath = false;
}
closeActiveSyncContext(activeSyncContext);
-
- final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
-
final long elapsedTime = SystemClock.elapsedRealtime() - activeSyncContext.mStartTime;
-
String historyMessage;
int downstreamActivity;
int upstreamActivity;
@@ -2544,6 +2781,12 @@ public class SyncManager {
} catch (RemoteException e) {
// we don't need to retry this in this case
}
+ } else if (activeSyncContext.mSyncServiceAdapter != null) {
+ try {
+ activeSyncContext.mSyncServiceAdapter.cancelSync(activeSyncContext);
+ } catch (RemoteException e) {
+ // we don't need to retry this in this case
+ }
}
historyMessage = SyncStorageEngine.MESG_CANCELED;
downstreamActivity = 0;
@@ -2553,24 +2796,35 @@ public class SyncManager {
stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage,
upstreamActivity, downstreamActivity, elapsedTime);
- if (syncResult != null && syncResult.tooManyDeletions) {
- installHandleTooManyDeletesNotification(syncOperation.account,
- syncOperation.authority, syncResult.stats.numDeletes,
- syncOperation.userId);
+ // Check for full-resync and schedule it after closing off the last sync.
+ if (info.target_provider) {
+ if (syncResult != null && syncResult.tooManyDeletions) {
+ installHandleTooManyDeletesNotification(info.account,
+ info.provider, syncResult.stats.numDeletes,
+ info.userId);
+ } else {
+ mNotificationMgr.cancelAsUser(null,
+ info.account.hashCode() ^ info.provider.hashCode(),
+ new UserHandle(info.userId));
+ }
+ if (syncResult != null && syncResult.fullSyncRequested) {
+ scheduleSyncOperation(
+ new SyncOperation(info.account, info.userId,
+ syncOperation.reason,
+ syncOperation.syncSource, info.provider, new Bundle(),
+ 0 /* delay */, 0 /* flex */,
+ syncOperation.backoff, syncOperation.delayUntil,
+ syncOperation.allowParallelSyncs));
+ }
} else {
- mNotificationMgr.cancelAsUser(null,
- syncOperation.account.hashCode() ^ syncOperation.authority.hashCode(),
- new UserHandle(syncOperation.userId));
- }
-
- if (syncResult != null && syncResult.fullSyncRequested) {
- scheduleSyncOperation(
- new SyncOperation(syncOperation.account, syncOperation.userId,
- syncOperation.reason,
- syncOperation.syncSource, syncOperation.authority, new Bundle(),
- 0 /* delay */, 0 /* flex */,
- syncOperation.backoff, syncOperation.delayUntil,
- syncOperation.allowParallelSyncs));
+ if (syncResult != null && syncResult.fullSyncRequested) {
+ scheduleSyncOperation(
+ new SyncOperation(info.service, info.userId,
+ syncOperation.reason,
+ syncOperation.syncSource, new Bundle(),
+ 0 /* delay */, 0 /* flex */,
+ syncOperation.backoff, syncOperation.delayUntil));
+ }
}
// no need to schedule an alarm, as that will be done by our caller.
}
@@ -2579,7 +2833,7 @@ public class SyncManager {
activeSyncContext.close();
mActiveSyncContexts.remove(activeSyncContext);
mSyncStorageEngine.removeActiveSync(activeSyncContext.mSyncInfo,
- activeSyncContext.mSyncOperation.userId);
+ activeSyncContext.mSyncOperation.target.userId);
}
/**
@@ -2842,26 +3096,16 @@ public class SyncManager {
}
public long insertStartSyncEvent(SyncOperation syncOperation) {
- final int source = syncOperation.syncSource;
final long now = System.currentTimeMillis();
-
- EventLog.writeEvent(2720, syncOperation.authority,
- SyncStorageEngine.EVENT_START, source,
- syncOperation.account.name.hashCode());
-
- return mSyncStorageEngine.insertStartSyncEvent(
- syncOperation.account, syncOperation.userId, syncOperation.reason,
- syncOperation.authority,
- now, source, syncOperation.isInitialization(), syncOperation.extras
- );
+ EventLog.writeEvent(2720,
+ syncOperation.toEventLog(SyncStorageEngine.EVENT_START));
+ return mSyncStorageEngine.insertStartSyncEvent(syncOperation, now);
}
public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage,
int upstreamActivity, int downstreamActivity, long elapsedTime) {
- EventLog.writeEvent(2720, syncOperation.authority,
- SyncStorageEngine.EVENT_STOP, syncOperation.syncSource,
- syncOperation.account.name.hashCode());
-
+ EventLog.writeEvent(2720,
+ syncOperation.toEventLog(SyncStorageEngine.EVENT_STOP));
mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime,
resultMessage, downstreamActivity, upstreamActivity);
}
@@ -2876,6 +3120,83 @@ public class SyncManager {
return false;
}
+ /**
+ * Sync extra comparison function.
+ * @param b1 bundle to compare
+ * @param b2 other bundle to compare
+ * @param includeSyncSettings if false, ignore system settings in bundle.
+ */
+ public static boolean syncExtrasEquals(Bundle b1, Bundle b2, boolean includeSyncSettings) {
+ if (b1 == b2) {
+ return true;
+ }
+ // Exit early if we can.
+ if (includeSyncSettings && b1.size() != b2.size()) {
+ return false;
+ }
+ Bundle bigger = b1.size() > b2.size() ? b1 : b2;
+ Bundle smaller = b1.size() > b2.size() ? b2 : b1;
+ for (String key : bigger.keySet()) {
+ if (!includeSyncSettings && isSyncSetting(key)) {
+ continue;
+ }
+ if (!smaller.containsKey(key)) {
+ return false;
+ }
+ if (!bigger.get(key).equals(smaller.get(key))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * TODO: Get rid of this when we separate sync settings extras from dev specified extras.
+ * @return true if the provided key is used by the SyncManager in scheduling the sync.
+ */
+ private static boolean isSyncSetting(String key) {
+ if (key.equals(ContentResolver.SYNC_EXTRAS_EXPEDITED)) {
+ return true;
+ }
+ if (key.equals(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS)) {
+ return true;
+ }
+ if (key.equals(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF)) {
+ return true;
+ }
+ if (key.equals(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY)) {
+ return true;
+ }
+ if (key.equals(ContentResolver.SYNC_EXTRAS_MANUAL)) {
+ return true;
+ }
+ if (key.equals(ContentResolver.SYNC_EXTRAS_UPLOAD)) {
+ return true;
+ }
+ if (key.equals(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS)) {
+ return true;
+ }
+ if (key.equals(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS)) {
+ return true;
+ }
+ if (key.equals(ContentResolver.SYNC_EXTRAS_EXPECTED_UPLOAD)) {
+ return true;
+ }
+ if (key.equals(ContentResolver.SYNC_EXTRAS_EXPECTED_DOWNLOAD)) {
+ return true;
+ }
+ if (key.equals(ContentResolver.SYNC_EXTRAS_PRIORITY)) {
+ return true;
+ }
+ if (key.equals(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED)) {
+ return true;
+ }
+ if (key.equals(ContentResolver.SYNC_EXTRAS_INITIALIZE)) {
+ return true;
+ }
+ return false;
+ }
+
static class PrintTable {
private ArrayList<Object[]> mTable = Lists.newArrayList();
private final int mCols;
@@ -2913,6 +3234,7 @@ public class SyncManager {
totalLength += maxLength;
formats[col] = String.format("%%-%ds", maxLength);
}
+ formats[mCols - 1] = "%s";
printRow(out, formats, mTable.get(0));
totalLength += (mCols - 1) * 2;
for (int i = 0; i < totalLength; ++i) {
diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java
index 4856747..5233014 100644
--- a/services/core/java/com/android/server/content/SyncOperation.java
+++ b/services/core/java/com/android/server/content/SyncOperation.java
@@ -20,10 +20,9 @@ import android.accounts.Account;
import android.content.pm.PackageManager;
import android.content.ComponentName;
import android.content.ContentResolver;
-import android.content.SyncRequest;
import android.os.Bundle;
import android.os.SystemClock;
-import android.util.Pair;
+import android.util.Log;
/**
* Value type that represents a sync operation.
@@ -32,10 +31,13 @@ import android.util.Pair;
* {@hide}
*/
public class SyncOperation implements Comparable {
+ public static final String TAG = "SyncManager";
+
public static final int REASON_BACKGROUND_DATA_SETTINGS_CHANGED = -1;
public static final int REASON_ACCOUNTS_UPDATED = -2;
public static final int REASON_SERVICE_CHANGED = -3;
public static final int REASON_PERIODIC = -4;
+ /** Sync started because it has just been set to isSyncable. */
public static final int REASON_IS_SYNCABLE = -5;
/** Sync started because it has just been set to sync automatically. */
public static final int REASON_SYNC_AUTO = -6;
@@ -54,19 +56,21 @@ public class SyncOperation implements Comparable {
"UserStart",
};
- /** Account info to identify a SyncAdapter registered with the system. */
- public final Account account;
- /** Authority info to identify a SyncAdapter registered with the system. */
- public final String authority;
- /** Service to which this operation will bind to perform the sync. */
- public final ComponentName service;
- public final int userId;
+ public static final int SYNC_TARGET_UNKNOWN = 0;
+ public static final int SYNC_TARGET_ADAPTER = 1;
+ public static final int SYNC_TARGET_SERVICE = 2;
+
+ /** Identifying info for the target for this operation. */
+ public final SyncStorageEngine.EndPoint target;
+ /** Why this sync was kicked off. {@link #REASON_NAMES} */
public final int reason;
+ /** Where this sync was initiated. */
public int syncSource;
public final boolean allowParallelSyncs;
public Bundle extras;
public final String key;
public boolean expedited;
+ /** Bare-bones version of this operation that is persisted across reboots. */
public SyncStorageEngine.PendingOperation pendingOperation;
/** Elapsed real time in millis at which to run this sync. */
public long latestRunTime;
@@ -79,25 +83,59 @@ public class SyncOperation implements Comparable {
* Depends on max(backoff, latestRunTime, and delayUntil).
*/
public long effectiveRunTime;
- /** Amount of time before {@link effectiveRunTime} from which this sync can run. */
+ /** Amount of time before {@link #effectiveRunTime} from which this sync can run. */
public long flexTime;
- public SyncOperation(Account account, int userId, int reason, int source, String authority,
+ /** Descriptive string key for this operation */
+ public String wakeLockName;
+
+ public SyncOperation(Account account, int userId, int reason, int source, String provider,
Bundle extras, long runTimeFromNow, long flexTime, long backoff,
long delayUntil, boolean allowParallelSyncs) {
- this.service = null;
- this.account = account;
- this.authority = authority;
- this.userId = userId;
+ this.target = new SyncStorageEngine.EndPoint(account, provider, userId);
this.reason = reason;
- this.syncSource = source;
this.allowParallelSyncs = allowParallelSyncs;
+ this.key = initialiseOperation(this.target, source, extras, runTimeFromNow, flexTime,
+ backoff, delayUntil);
+ }
+
+ public SyncOperation(ComponentName service, int userId, int reason, int source,
+ Bundle extras, long runTimeFromNow, long flexTime, long backoff,
+ long delayUntil) {
+ this.target = new SyncStorageEngine.EndPoint(service, userId);
+ // Default to true for sync service. The service itself decides how to handle this.
+ this.allowParallelSyncs = true;
+ this.reason = reason;
+ this.key =
+ initialiseOperation(this.target,
+ source, extras, runTimeFromNow, flexTime, backoff, delayUntil);
+ }
+
+ /** Used to reschedule a sync at a new point in time. */
+ SyncOperation(SyncOperation other, long newRunTimeFromNow) {
+ this.target = other.target;
+ this.reason = other.reason;
+ this.expedited = other.expedited;
+ this.allowParallelSyncs = other.allowParallelSyncs;
+ // re-use old flex, but only
+ long newFlexTime = Math.min(other.flexTime, newRunTimeFromNow);
+ this.key =
+ initialiseOperation(this.target,
+ other.syncSource, other.extras,
+ newRunTimeFromNow /* runTimeFromNow*/,
+ newFlexTime /* flexTime */,
+ other.backoff,
+ 0L /* delayUntil */);
+ }
+
+ private String initialiseOperation(SyncStorageEngine.EndPoint info, int source, Bundle extras,
+ long runTimeFromNow, long flexTime, long backoff, long delayUntil) {
+ this.syncSource = source;
this.extras = new Bundle(extras);
cleanBundle(this.extras);
this.delayUntil = delayUntil;
this.backoff = backoff;
final long now = SystemClock.elapsedRealtime();
- // Checks the extras bundle. Must occur after we set the internal bundle.
if (runTimeFromNow < 0 || isExpedited()) {
this.expedited = true;
this.latestRunTime = now;
@@ -108,7 +146,11 @@ public class SyncOperation implements Comparable {
this.flexTime = flexTime;
}
updateEffectiveRunTime();
- this.key = toKey();
+ return toKey(info, this.extras);
+ }
+
+ public boolean matchesAuthority(SyncOperation other) {
+ return this.target.matchesSpec(other.target);
}
/**
@@ -126,10 +168,6 @@ public class SyncOperation implements Comparable {
removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_EXPEDITED);
removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS);
removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_DISALLOW_METERED);
-
- // Remove Config data.
- bundle.remove(ContentResolver.SYNC_EXTRAS_EXPECTED_UPLOAD);
- bundle.remove(ContentResolver.SYNC_EXTRAS_EXPECTED_DOWNLOAD);
}
private void removeFalseExtra(Bundle bundle, String extraName) {
@@ -138,22 +176,24 @@ public class SyncOperation implements Comparable {
}
}
- /** Only used to immediately reschedule a sync. */
- SyncOperation(SyncOperation other) {
- this.service = other.service;
- this.account = other.account;
- this.authority = other.authority;
- this.userId = other.userId;
- this.reason = other.reason;
- this.syncSource = other.syncSource;
- this.extras = new Bundle(other.extras);
- this.expedited = other.expedited;
- this.latestRunTime = SystemClock.elapsedRealtime();
- this.flexTime = 0L;
- this.backoff = other.backoff;
- this.allowParallelSyncs = other.allowParallelSyncs;
- this.updateEffectiveRunTime();
- this.key = toKey();
+ /**
+ * Determine whether if this sync operation is running, the provided operation would conflict
+ * with it.
+ * Parallel syncs allow multiple accounts to be synced at the same time.
+ */
+ public boolean isConflict(SyncOperation toRun) {
+ final SyncStorageEngine.EndPoint other = toRun.target;
+ if (target.target_provider) {
+ return target.account.type.equals(other.account.type)
+ && target.provider.equals(other.provider)
+ && target.userId == other.userId
+ && (!allowParallelSyncs
+ || target.account.name.equals(other.account.name));
+ } else {
+ // Ops that target a service default to allow parallel syncs, which is handled by the
+ // service returning SYNC_IN_PROGRESS if they don't.
+ return target.service.equals(other.service) && !allowParallelSyncs;
+ }
}
@Override
@@ -162,18 +202,26 @@ public class SyncOperation implements Comparable {
}
public String dump(PackageManager pm, boolean useOneLine) {
- StringBuilder sb = new StringBuilder()
- .append(account.name)
+ StringBuilder sb = new StringBuilder();
+ if (target.target_provider) {
+ sb.append(target.account.name)
.append(" u")
- .append(userId).append(" (")
- .append(account.type)
+ .append(target.userId).append(" (")
+ .append(target.account.type)
.append(")")
.append(", ")
- .append(authority)
- .append(", ")
- .append(SyncStorageEngine.SOURCES[syncSource])
- .append(", latestRunTime ")
- .append(latestRunTime);
+ .append(target.provider)
+ .append(", ");
+ } else if (target.target_service) {
+ sb.append(target.service.getPackageName())
+ .append(" u")
+ .append(target.userId).append(" (")
+ .append(target.service.getClassName()).append(")")
+ .append(", ");
+ }
+ sb.append(SyncStorageEngine.SOURCES[syncSource])
+ .append(", currentRunTime ")
+ .append(effectiveRunTime);
if (expedited) {
sb.append(", EXPEDITED");
}
@@ -211,10 +259,6 @@ public class SyncOperation implements Comparable {
}
}
- public boolean isMeteredDisallowed() {
- return extras.getBoolean(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED, false);
- }
-
public boolean isInitialization() {
return extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false);
}
@@ -227,28 +271,39 @@ public class SyncOperation implements Comparable {
return extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false);
}
+ public boolean isNotAllowedOnMetered() {
+ return extras.getBoolean(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED, false);
+ }
+
/** Changed in V3. */
- private String toKey() {
+ public static String toKey(SyncStorageEngine.EndPoint info, Bundle extras) {
StringBuilder sb = new StringBuilder();
- if (service == null) {
- sb.append("authority: ").append(authority);
- sb.append(" account {name=" + account.name + ", user=" + userId + ", type=" + account.type
+ if (info.target_provider) {
+ sb.append("provider: ").append(info.provider);
+ sb.append(" account {name=" + info.account.name
+ + ", user="
+ + info.userId
+ + ", type="
+ + info.account.type
+ "}");
- } else {
+ } else if (info.target_service) {
sb.append("service {package=" )
- .append(service.getPackageName())
+ .append(info.service.getPackageName())
.append(" user=")
- .append(userId)
+ .append(info.userId)
.append(", class=")
- .append(service.getClassName())
+ .append(info.service.getClassName())
.append("}");
+ } else {
+ Log.v(TAG, "Converting SyncOperaton to key, invalid target: " + info.toString());
+ return "";
}
sb.append(" extras: ");
extrasToStringBuilder(extras, sb);
return sb.toString();
}
- public static void extrasToStringBuilder(Bundle bundle, StringBuilder sb) {
+ private static void extrasToStringBuilder(Bundle bundle, StringBuilder sb) {
sb.append("[");
for (String key : bundle.keySet()) {
sb.append(key).append("=").append(bundle.get(key)).append(" ");
@@ -256,6 +311,23 @@ public class SyncOperation implements Comparable {
sb.append("]");
}
+ public String wakeLockName() {
+ if (wakeLockName != null) {
+ return wakeLockName;
+ }
+ if (target.target_provider) {
+ return (wakeLockName = target.provider
+ + "/" + target.account.type
+ + "/" + target.account.name);
+ } else if (target.target_service) {
+ return (wakeLockName = target.service.getPackageName()
+ + "/" + target.service.getClassName());
+ } else {
+ Log.wtf(TAG, "Invalid target getting wakelock name for operation - " + key);
+ return null;
+ }
+ }
+
/**
* Update the effective run time of this Operation based on latestRunTime (specified at
* creation time of sync), delayUntil (specified by SyncAdapter), or backoff (specified by
@@ -291,4 +363,21 @@ public class SyncOperation implements Comparable {
return 0;
}
}
+
+ // TODO: Test this to make sure that casting to object doesn't lose the type info for EventLog.
+ public Object[] toEventLog(int event) {
+ Object[] logArray = new Object[4];
+ logArray[1] = event;
+ logArray[2] = syncSource;
+ if (target.target_provider) {
+ logArray[0] = target.provider;
+ logArray[3] = target.account.name.hashCode();
+ } else if (target.target_service) {
+ logArray[0] = target.service.getPackageName();
+ logArray[3] = target.service.hashCode();
+ } else {
+ Log.wtf(TAG, "sync op with invalid target: " + key);
+ }
+ return logArray;
+ }
}
diff --git a/services/core/java/com/android/server/content/SyncQueue.java b/services/core/java/com/android/server/content/SyncQueue.java
index 6f3fe6e..5d93882 100644
--- a/services/core/java/com/android/server/content/SyncQueue.java
+++ b/services/core/java/com/android/server/content/SyncQueue.java
@@ -16,12 +16,11 @@
package com.android.server.content;
-import android.accounts.Account;
import android.content.pm.PackageManager;
-import android.content.pm.RegisteredServicesCache;
import android.content.SyncAdapterType;
import android.content.SyncAdaptersCache;
import android.content.pm.RegisteredServicesCache.ServiceInfo;
+import android.os.Bundle;
import android.os.SystemClock;
import android.text.format.DateUtils;
import android.util.Log;
@@ -60,25 +59,51 @@ public class SyncQueue {
public void addPendingOperations(int userId) {
for (SyncStorageEngine.PendingOperation op : mSyncStorageEngine.getPendingOperations()) {
- if (op.userId != userId) continue;
-
- final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(
- op.account, op.userId, op.authority);
- final ServiceInfo<SyncAdapterType> syncAdapterInfo = mSyncAdapters.getServiceInfo(
- SyncAdapterType.newKey(op.authority, op.account.type), op.userId);
- if (syncAdapterInfo == null) {
- Log.w(TAG, "Missing sync adapter info for authority " + op.authority + ", userId "
- + op.userId);
- continue;
+ final SyncStorageEngine.EndPoint info = op.target;
+ if (info.userId != userId) continue;
+
+ final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(info);
+ SyncOperation operationToAdd;
+ if (info.target_provider) {
+ final ServiceInfo<SyncAdapterType> syncAdapterInfo = mSyncAdapters.getServiceInfo(
+ SyncAdapterType.newKey(info.provider, info.account.type), info.userId);
+ if (syncAdapterInfo == null) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Missing sync adapter info for authority " + op.target);
+ }
+ continue;
+ }
+ operationToAdd = new SyncOperation(
+ info.account, info.userId, op.reason, op.syncSource, info.provider,
+ op.extras,
+ 0 /* delay */,
+ 0 /* flex */,
+ backoff != null ? backoff.first : 0,
+ mSyncStorageEngine.getDelayUntilTime(info),
+ syncAdapterInfo.type.allowParallelSyncs());
+ operationToAdd.expedited = op.expedited;
+ operationToAdd.pendingOperation = op;
+ add(operationToAdd, op);
+ } else if (info.target_service) {
+ try {
+ mPackageManager.getServiceInfo(info.service, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.w(TAG, "Missing sync service for authority " + op.target);
+ }
+ continue;
+ }
+ operationToAdd = new SyncOperation(
+ info.service, info.userId, op.reason, op.syncSource,
+ op.extras,
+ 0 /* delay */,
+ 0 /* flex */,
+ backoff != null ? backoff.first : 0,
+ mSyncStorageEngine.getDelayUntilTime(info));
+ operationToAdd.expedited = op.expedited;
+ operationToAdd.pendingOperation = op;
+ add(operationToAdd, op);
}
- SyncOperation syncOperation = new SyncOperation(
- op.account, op.userId, op.reason, op.syncSource, op.authority, op.extras,
- 0 /* delay */, 0 /* flex */, backoff != null ? backoff.first : 0,
- mSyncStorageEngine.getDelayUntilTime(op.account, op.userId, op.authority),
- syncAdapterInfo.type.allowParallelSyncs());
- syncOperation.expedited = op.expedited;
- syncOperation.pendingOperation = op;
- add(syncOperation, op);
}
}
@@ -119,12 +144,8 @@ public class SyncQueue {
operation.pendingOperation = pop;
// Don't update the PendingOp if one already exists. This really is just a placeholder,
// no actual scheduling info is placed here.
- // TODO: Change this to support service components.
if (operation.pendingOperation == null) {
- pop = new SyncStorageEngine.PendingOperation(
- operation.account, operation.userId, operation.reason, operation.syncSource,
- operation.authority, operation.extras, operation.expedited);
- pop = mSyncStorageEngine.insertIntoPending(pop);
+ pop = mSyncStorageEngine.insertIntoPending(operation);
if (pop == null) {
throw new IllegalStateException("error adding pending sync operation "
+ operation);
@@ -136,17 +157,16 @@ public class SyncQueue {
return true;
}
- public void removeUser(int userId) {
+ public void removeUserLocked(int userId) {
ArrayList<SyncOperation> opsToRemove = new ArrayList<SyncOperation>();
for (SyncOperation op : mOperationsMap.values()) {
- if (op.userId == userId) {
+ if (op.target.userId == userId) {
opsToRemove.add(op);
}
}
-
- for (SyncOperation op : opsToRemove) {
- remove(op);
- }
+ for (SyncOperation op : opsToRemove) {
+ remove(op);
+ }
}
/**
@@ -154,8 +174,15 @@ public class SyncQueue {
* @param operation the operation to remove
*/
public void remove(SyncOperation operation) {
+ boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
SyncOperation operationToRemove = mOperationsMap.remove(operation.key);
+ if (isLoggable) {
+ Log.v(TAG, "Attempting to remove: " + operation.key);
+ }
if (operationToRemove == null) {
+ if (isLoggable) {
+ Log.v(TAG, "Could not find: " + operation.key);
+ }
return;
}
if (!mSyncStorageEngine.deleteFromPending(operationToRemove.pendingOperation)) {
@@ -164,41 +191,58 @@ public class SyncQueue {
}
}
- public void onBackoffChanged(Account account, int userId, String providerName, long backoff) {
- // for each op that matches the account and provider update its
+ /** Reset backoffs for all operations in the queue. */
+ public void clearBackoffs() {
+ for (SyncOperation op : mOperationsMap.values()) {
+ op.backoff = 0L;
+ op.updateEffectiveRunTime();
+ }
+ }
+
+ public void onBackoffChanged(SyncStorageEngine.EndPoint target, long backoff) {
+ // For each op that matches the target of the changed op, update its
// backoff and effectiveStartTime
for (SyncOperation op : mOperationsMap.values()) {
- if (op.account.equals(account) && op.authority.equals(providerName)
- && op.userId == userId) {
+ if (op.target.matchesSpec(target)) {
op.backoff = backoff;
op.updateEffectiveRunTime();
}
}
}
- public void onDelayUntilTimeChanged(Account account, String providerName, long delayUntil) {
- // for each op that matches the account and provider update its
- // delayUntilTime and effectiveStartTime
+ public void onDelayUntilTimeChanged(SyncStorageEngine.EndPoint target, long delayUntil) {
+ // for each op that matches the target info of the provided op, change the delay time.
for (SyncOperation op : mOperationsMap.values()) {
- if (op.account.equals(account) && op.authority.equals(providerName)) {
+ if (op.target.matchesSpec(target)) {
op.delayUntil = delayUntil;
op.updateEffectiveRunTime();
}
}
}
- public void remove(Account account, int userId, String authority) {
+ /**
+ * Remove all of the SyncOperations associated with a given target.
+ *
+ * @param info target object provided here can have null Account/provider. This is the case
+ * where you want to remove all ops associated with a provider (null Account) or all ops
+ * associated with an account (null provider).
+ * @param extras option bundle to include to further specify which operation to remove. If this
+ * bundle contains sync settings flags, they are ignored.
+ */
+ public void remove(final SyncStorageEngine.EndPoint info, Bundle extras) {
Iterator<Map.Entry<String, SyncOperation>> entries = mOperationsMap.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<String, SyncOperation> entry = entries.next();
SyncOperation syncOperation = entry.getValue();
- if (account != null && !syncOperation.account.equals(account)) {
- continue;
- }
- if (authority != null && !syncOperation.authority.equals(authority)) {
+ final SyncStorageEngine.EndPoint opInfo = syncOperation.target;
+ if (!opInfo.matchesSpec(info)) {
continue;
}
- if (userId != syncOperation.userId) {
+ if (extras != null
+ && !SyncManager.syncExtrasEquals(
+ syncOperation.extras,
+ extras,
+ false /* no config flags*/)) {
continue;
}
entries.remove();
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 124bc60..5df7fc6 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -24,6 +24,7 @@ import android.content.Context;
import android.content.ISyncStatusObserver;
import android.content.PeriodicSync;
import android.content.SyncInfo;
+import android.content.SyncRequest;
import android.content.SyncStatusInfo;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
@@ -36,10 +37,12 @@ import android.os.Message;
import android.os.Parcel;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
+import android.util.ArrayMap;
import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
@@ -71,7 +74,6 @@ import java.util.TimeZone;
public class SyncStorageEngine extends Handler {
private static final String TAG = "SyncManager";
- private static final boolean DEBUG = false;
private static final String TAG_FILE = "SyncManagerFile";
private static final String XML_ATTR_NEXT_AUTHORITY_ID = "nextAuthorityId";
@@ -108,10 +110,7 @@ public class SyncStorageEngine extends Handler {
/** Enum value for a local-initiated sync. */
public static final int SOURCE_LOCAL = 1;
- /**
- * Enum value for a poll-based sync (e.g., upon connection to
- * network)
- */
+ /** Enum value for a poll-based sync (e.g., upon connection to network) */
public static final int SOURCE_POLL = 2;
/** Enum value for a user-initiated sync. */
@@ -119,6 +118,9 @@ public class SyncStorageEngine extends Handler {
/** Enum value for a periodic sync. */
public static final int SOURCE_PERIODIC = 4;
+
+ /** Enum value for a sync started for a service. */
+ public static final int SOURCE_SERVICE = 5;
public static final long NOT_IN_BACKOFF_MODE = -1;
@@ -128,7 +130,8 @@ public class SyncStorageEngine extends Handler {
"LOCAL",
"POLL",
"USER",
- "PERIODIC" };
+ "PERIODIC",
+ "SERVICE"};
// The MESG column will contain one of these or one of the Error types.
public static final String MESG_SUCCESS = "success";
@@ -156,41 +159,54 @@ public class SyncStorageEngine extends Handler {
}
public static class PendingOperation {
- final Account account;
- final int userId;
+ final EndPoint target;
final int reason;
final int syncSource;
- final String authority;
final Bundle extras; // note: read-only.
- final ComponentName serviceName;
final boolean expedited;
- int authorityId;
+ final int authorityId;
+ // No longer used.
+ // Keep around for sake up updating from pending.bin to pending.xml
byte[] flatExtras;
- PendingOperation(Account account, int userId, int reason, int source,
- String authority, Bundle extras, boolean expedited) {
- this.account = account;
- this.userId = userId;
+ PendingOperation(AuthorityInfo authority, int reason, int source,
+ Bundle extras, boolean expedited) {
+ this.target = authority.target;
this.syncSource = source;
this.reason = reason;
- this.authority = authority;
this.extras = extras != null ? new Bundle(extras) : extras;
this.expedited = expedited;
- this.authorityId = -1;
- this.serviceName = null;
+ this.authorityId = authority.ident;
}
PendingOperation(PendingOperation other) {
- this.account = other.account;
- this.userId = other.userId;
this.reason = other.reason;
this.syncSource = other.syncSource;
- this.authority = other.authority;
+ this.target = other.target;
this.extras = other.extras;
this.authorityId = other.authorityId;
this.expedited = other.expedited;
- this.serviceName = other.serviceName;
+ }
+
+ /**
+ * Considered equal if they target the same sync adapter (A
+ * {@link android.content.SyncService}
+ * is considered an adapter), for the same userId.
+ * @param other PendingOperation to compare.
+ * @return true if the two pending ops are the same.
+ */
+ public boolean equals(PendingOperation other) {
+ return target.matchesSpec(other.target);
+ }
+
+ public String toString() {
+ return "service=" + target.service
+ + " user=" + target.userId
+ + " auth=" + target
+ + " account=" + target.account
+ + " src=" + syncSource
+ + " extras=" + extras;
}
}
@@ -204,17 +220,96 @@ public class SyncStorageEngine extends Handler {
}
}
- public static class AuthorityInfo {
+ /** Bare bones representation of a sync target. */
+ public static class EndPoint {
+ public final static EndPoint USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL =
+ new EndPoint(null, null, UserHandle.USER_ALL);
final ComponentName service;
final Account account;
final int userId;
- final String authority;
+ final String provider;
+ final boolean target_service;
+ final boolean target_provider;
+
+ public EndPoint(ComponentName service, int userId) {
+ this.service = service;
+ this.userId = userId;
+ this.account = null;
+ this.provider = null;
+ this.target_service = true;
+ this.target_provider = false;
+ }
+
+ public EndPoint(Account account, String provider, int userId) {
+ this.account = account;
+ this.provider = provider;
+ this.userId = userId;
+ this.service = null;
+ this.target_service = false;
+ this.target_provider = true;
+ }
+
+ /**
+ * An Endpoint for a sync matches if it targets the same sync adapter for the same user.
+ *
+ * @param spec the Endpoint to match. If the spec has null fields, they indicate a wildcard
+ * and match any.
+ */
+ public boolean matchesSpec(EndPoint spec) {
+ if (userId != spec.userId
+ && userId != UserHandle.USER_ALL
+ && spec.userId != UserHandle.USER_ALL) {
+ return false;
+ }
+ if (target_service && spec.target_service) {
+ return service.equals(spec.service);
+ } else if (target_provider && spec.target_provider) {
+ boolean accountsMatch;
+ if (spec.account == null) {
+ accountsMatch = true;
+ } else {
+ accountsMatch = account.equals(spec.account);
+ }
+ boolean providersMatch;
+ if (spec.provider == null) {
+ providersMatch = true;
+ } else {
+ providersMatch = provider.equals(spec.provider);
+ }
+ return accountsMatch && providersMatch;
+ }
+ return false;
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ if (target_provider) {
+ sb.append(account == null ? "ALL ACCS" : account.name)
+ .append("/")
+ .append(provider == null ? "ALL PDRS" : provider);
+ } else if (target_service) {
+ sb.append(service.getPackageName() + "/")
+ .append(service.getClassName());
+ } else {
+ sb.append("invalid target");
+ }
+ sb.append(":u" + userId);
+ return sb.toString();
+ }
+ }
+
+ public static class AuthorityInfo {
+ final EndPoint target;
final int ident;
boolean enabled;
int syncable;
+ /** Time at which this sync will run, taking into account backoff. */
long backoffTime;
+ /** Amount of delay due to backoff. */
long backoffDelay;
+ /** Time offset to add to any requests coming to this target. */
long delayUntil;
+
final ArrayList<PeriodicSync> periodicSyncs;
/**
@@ -224,10 +319,7 @@ public class SyncStorageEngine extends Handler {
* @param toCopy AuthorityInfo to be copied.
*/
AuthorityInfo(AuthorityInfo toCopy) {
- account = toCopy.account;
- userId = toCopy.userId;
- authority = toCopy.authority;
- service = toCopy.service;
+ target = toCopy.target;
ident = toCopy.ident;
enabled = toCopy.enabled;
syncable = toCopy.syncable;
@@ -241,56 +333,40 @@ public class SyncStorageEngine extends Handler {
}
}
- /**
- * Create an authority with one periodic sync scheduled with an empty bundle and syncing
- * every day. An empty bundle is considered equal to any other bundle see
- * {@link PeriodicSync.syncExtrasEquals}.
- * @param account Account that this authority syncs.
- * @param userId which user this sync is registered for.
- * @param userId user for which this authority is registered.
- * @param ident id of this authority.
- */
- AuthorityInfo(Account account, int userId, String authority, int ident) {
- this.account = account;
- this.userId = userId;
- this.authority = authority;
- this.service = null;
- this.ident = ident;
- enabled = SYNC_ENABLED_DEFAULT;
- syncable = -1; // default to "unknown"
- backoffTime = -1; // if < 0 then we aren't in backoff mode
- backoffDelay = -1; // if < 0 then we aren't in backoff mode
+ AuthorityInfo(EndPoint info, int id) {
+ target = info;
+ ident = id;
+ enabled = info.target_provider ?
+ SYNC_ENABLED_DEFAULT : true;
+ // Service is active by default,
+ if (info.target_service) {
+ this.syncable = 1;
+ }
periodicSyncs = new ArrayList<PeriodicSync>();
- // Old version adds one periodic sync a day.
- periodicSyncs.add(new PeriodicSync(account, authority,
- new Bundle(),
- DEFAULT_POLL_FREQUENCY_SECONDS,
- calculateDefaultFlexTime(DEFAULT_POLL_FREQUENCY_SECONDS)));
+ defaultInitialisation();
}
- /**
- * Create an authority with one periodic sync scheduled with an empty bundle and syncing
- * every day using a sync service.
- * @param cname sync service identifier.
- * @param userId user for which this authority is registered.
- * @param ident id of this authority.
- */
- AuthorityInfo(ComponentName cname, int userId, int ident) {
- this.account = null;
- this.userId = userId;
- this.authority = null;
- this.service = cname;
- this.ident = ident;
- // Sync service is always enabled.
- enabled = true;
+ private void defaultInitialisation() {
syncable = -1; // default to "unknown"
backoffTime = -1; // if < 0 then we aren't in backoff mode
backoffDelay = -1; // if < 0 then we aren't in backoff mode
- periodicSyncs = new ArrayList<PeriodicSync>();
- periodicSyncs.add(new PeriodicSync(account, authority,
- new Bundle(),
- DEFAULT_POLL_FREQUENCY_SECONDS,
- calculateDefaultFlexTime(DEFAULT_POLL_FREQUENCY_SECONDS)));
+ PeriodicSync defaultSync;
+ // Old version is one sync a day. Empty bundle gets replaced by any addPeriodicSync()
+ // call.
+ if (target.target_provider) {
+ defaultSync =
+ new PeriodicSync(target.account, target.provider,
+ new Bundle(),
+ DEFAULT_POLL_FREQUENCY_SECONDS,
+ calculateDefaultFlexTime(DEFAULT_POLL_FREQUENCY_SECONDS));
+ periodicSyncs.add(defaultSync);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return target + ", enabled=" + enabled + ", syncable=" + syncable + ", backoff="
+ + backoffTime + ", delay=" + delayUntil;
}
}
@@ -322,16 +398,9 @@ public class SyncStorageEngine extends Handler {
}
interface OnSyncRequestListener {
- /**
- * Called when a sync is needed on an account(s) due to some change in state.
- * @param account
- * @param userId
- * @param reason
- * @param authority
- * @param extras
- */
- public void onSyncRequest(Account account, int userId, int reason, String authority,
- Bundle extras);
+
+ /** Called when a sync is needed on an account(s) due to some change in state. */
+ public void onSyncRequest(EndPoint info, int reason, Bundle extras);
}
// Primary list of all syncable authorities. Also our global lock.
@@ -356,9 +425,9 @@ public class SyncStorageEngine extends Handler {
private final RemoteCallbackList<ISyncStatusObserver> mChangeListeners
= new RemoteCallbackList<ISyncStatusObserver>();
- /** Reverse mapping for component name -> <userid -> authority id>. */
- private final HashMap<ComponentName, SparseArray<AuthorityInfo>> mServices =
- new HashMap<ComponentName, SparseArray<AuthorityInfo>>();
+ /** Reverse mapping for component name -> <userid -> target id>. */
+ private final ArrayMap<ComponentName, SparseArray<AuthorityInfo>> mServices =
+ new ArrayMap<ComponentName, SparseArray<AuthorityInfo>>();
private int mNextAuthorityId = 0;
@@ -501,7 +570,7 @@ public class SyncStorageEngine extends Handler {
* @return amount of seconds before syncTimeSeconds that the sync can occur.
* I.e.
* earliest_sync_time = syncTimeSeconds - calculateDefaultFlexTime(syncTimeSeconds)
- * The flex time is capped at a percentage of the {@link DEFAULT_POLL_FREQUENCY_SECONDS}.
+ * The flex time is capped at a percentage of the {@link #DEFAULT_POLL_FREQUENCY_SECONDS}.
*/
public static long calculateDefaultFlexTime(long syncTimeSeconds) {
if (syncTimeSeconds < DEFAULT_MIN_FLEX_ALLOWED_SECS) {
@@ -535,7 +604,7 @@ public class SyncStorageEngine extends Handler {
mChangeListeners.finishBroadcast();
}
- if (DEBUG) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "reportChange " + which + " to: " + reports);
}
@@ -555,7 +624,8 @@ public class SyncStorageEngine extends Handler {
public boolean getSyncAutomatically(Account account, int userId, String providerName) {
synchronized (mAuthorities) {
if (account != null) {
- AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
+ AuthorityInfo authority = getAuthorityLocked(
+ new EndPoint(account, providerName, userId),
"getSyncAutomatically");
return authority != null && authority.enabled;
}
@@ -563,10 +633,9 @@ public class SyncStorageEngine extends Handler {
int i = mAuthorities.size();
while (i > 0) {
i--;
- AuthorityInfo authority = mAuthorities.valueAt(i);
- if (authority.authority.equals(providerName)
- && authority.userId == userId
- && authority.enabled) {
+ AuthorityInfo authorityInfo = mAuthorities.valueAt(i);
+ if (authorityInfo.target.matchesSpec(new EndPoint(account, providerName, userId))
+ && authorityInfo.enabled) {
return true;
}
}
@@ -576,15 +645,18 @@ public class SyncStorageEngine extends Handler {
public void setSyncAutomatically(Account account, int userId, String providerName,
boolean sync) {
- if (DEBUG) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.d(TAG, "setSyncAutomatically: " + /* account + */" provider " + providerName
+ ", user " + userId + " -> " + sync);
}
synchronized (mAuthorities) {
- AuthorityInfo authority = getOrCreateAuthorityLocked(account, userId, providerName, -1,
- false);
+ AuthorityInfo authority =
+ getOrCreateAuthorityLocked(
+ new EndPoint(account, providerName, userId),
+ -1 /* ident */,
+ false);
if (authority.enabled == sync) {
- if (DEBUG) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.d(TAG, "setSyncAutomatically: already set to " + sync + ", doing nothing");
}
return;
@@ -603,8 +675,9 @@ public class SyncStorageEngine extends Handler {
public int getIsSyncable(Account account, int userId, String providerName) {
synchronized (mAuthorities) {
if (account != null) {
- AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
- "getIsSyncable");
+ AuthorityInfo authority = getAuthorityLocked(
+ new EndPoint(account, providerName, userId),
+ "get authority syncable");
if (authority == null) {
return -1;
}
@@ -614,9 +687,10 @@ public class SyncStorageEngine extends Handler {
int i = mAuthorities.size();
while (i > 0) {
i--;
- AuthorityInfo authority = mAuthorities.valueAt(i);
- if (authority.authority.equals(providerName)) {
- return authority.syncable;
+ AuthorityInfo authorityInfo = mAuthorities.valueAt(i);
+ if (authorityInfo.target != null
+ && authorityInfo.target.provider.equals(providerName)) {
+ return authorityInfo.syncable;
}
}
return -1;
@@ -624,119 +698,178 @@ public class SyncStorageEngine extends Handler {
}
public void setIsSyncable(Account account, int userId, String providerName, int syncable) {
- if (syncable > 1) {
- syncable = 1;
- } else if (syncable < -1) {
- syncable = -1;
- }
- if (DEBUG) {
- Log.d(TAG, "setIsSyncable: " + account + ", provider " + providerName
- + ", user " + userId + " -> " + syncable);
+ setSyncableStateForEndPoint(new EndPoint(account, providerName, userId), syncable);
+ }
+
+ public boolean getIsTargetServiceActive(ComponentName cname, int userId) {
+ synchronized (mAuthorities) {
+ if (cname != null) {
+ AuthorityInfo authority = getAuthorityLocked(
+ new EndPoint(cname, userId),
+ "get service active");
+ if (authority == null) {
+ return false;
+ }
+ return (authority.syncable == 1);
+ }
+ return false;
}
+ }
+
+ public void setIsTargetServiceActive(ComponentName cname, int userId, boolean active) {
+ setSyncableStateForEndPoint(new EndPoint(cname, userId), active ? 1 : 0);
+ }
+
+ /**
+ * An enabled sync service and a syncable provider's adapter both get resolved to the same
+ * persisted variable - namely the "syncable" attribute for an AuthorityInfo in accounts.xml.
+ * @param target target to set value for.
+ * @param syncable 0 indicates unsyncable, <0 unknown, >0 is active/syncable.
+ */
+ private void setSyncableStateForEndPoint(EndPoint target, int syncable) {
+ AuthorityInfo aInfo;
synchronized (mAuthorities) {
- AuthorityInfo authority =
- getOrCreateAuthorityLocked(account, userId, providerName, -1, false);
- if (authority.syncable == syncable) {
- if (DEBUG) {
+ aInfo = getOrCreateAuthorityLocked(target, -1, false);
+ if (syncable > 1) {
+ syncable = 1;
+ } else if (syncable < -1) {
+ syncable = -1;
+ }
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.d(TAG, "setIsSyncable: " + aInfo.toString() + " -> " + syncable);
+ }
+ if (aInfo.syncable == syncable) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.d(TAG, "setIsSyncable: already set to " + syncable + ", doing nothing");
}
return;
}
- authority.syncable = syncable;
+ aInfo.syncable = syncable;
writeAccountInfoLocked();
}
-
if (syncable > 0) {
- requestSync(account, userId, SyncOperation.REASON_IS_SYNCABLE, providerName,
- new Bundle());
+ requestSync(aInfo, SyncOperation.REASON_IS_SYNCABLE, new Bundle());
}
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
}
- public Pair<Long, Long> getBackoff(Account account, int userId, String providerName) {
+ public Pair<Long, Long> getBackoff(EndPoint info) {
synchronized (mAuthorities) {
- AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
- "getBackoff");
- if (authority == null || authority.backoffTime < 0) {
- return null;
+ AuthorityInfo authority = getAuthorityLocked(info, "getBackoff");
+ if (authority != null) {
+ return Pair.create(authority.backoffTime, authority.backoffDelay);
}
- return Pair.create(authority.backoffTime, authority.backoffDelay);
+ return null;
}
}
- public void setBackoff(Account account, int userId, String providerName,
- long nextSyncTime, long nextDelay) {
- if (DEBUG) {
- Log.v(TAG, "setBackoff: " + account + ", provider " + providerName
- + ", user " + userId
+ /**
+ * Update the backoff for the given endpoint. The endpoint may be for a provider/account and
+ * the account or provider info be null, which signifies all accounts or providers.
+ */
+ public void setBackoff(EndPoint info, long nextSyncTime, long nextDelay) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "setBackoff: " + info
+ " -> nextSyncTime " + nextSyncTime + ", nextDelay " + nextDelay);
}
- boolean changed = false;
+ boolean changed;
synchronized (mAuthorities) {
- if (account == null || providerName == null) {
- for (AccountInfo accountInfo : mAccounts.values()) {
- if (account != null && !account.equals(accountInfo.accountAndUser.account)
- && userId != accountInfo.accountAndUser.userId) {
- continue;
- }
- for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) {
- if (providerName != null
- && !providerName.equals(authorityInfo.authority)) {
- continue;
- }
- if (authorityInfo.backoffTime != nextSyncTime
- || authorityInfo.backoffDelay != nextDelay) {
- authorityInfo.backoffTime = nextSyncTime;
- authorityInfo.backoffDelay = nextDelay;
- changed = true;
- }
- }
- }
+ if (info.target_provider
+ && (info.account == null || info.provider == null)) {
+ // Do more work for a provider sync if the provided info has specified all
+ // accounts/providers.
+ changed = setBackoffLocked(
+ info.account /* may be null */,
+ info.userId,
+ info.provider /* may be null */,
+ nextSyncTime, nextDelay);
} else {
- AuthorityInfo authority =
- getOrCreateAuthorityLocked(account, userId, providerName, -1 /* ident */,
- true);
- if (authority.backoffTime == nextSyncTime && authority.backoffDelay == nextDelay) {
- return;
+ AuthorityInfo authorityInfo =
+ getOrCreateAuthorityLocked(info, -1 /* ident */, true);
+ if (authorityInfo.backoffTime == nextSyncTime
+ && authorityInfo.backoffDelay == nextDelay) {
+ changed = false;
+ } else {
+ authorityInfo.backoffTime = nextSyncTime;
+ authorityInfo.backoffDelay = nextDelay;
+ changed = true;
}
- authority.backoffTime = nextSyncTime;
- authority.backoffDelay = nextDelay;
- changed = true;
}
}
-
if (changed) {
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
}
}
/**
- * Callers of this function need to hold a lock for syncQueue object passed in. Bear in mind
- * this function grabs the lock for {@link #mAuthorities}
- * @param syncQueue queue containing pending sync operations.
+ * Either set backoff for a specific authority, or set backoff for all the
+ * accounts on a specific adapter/all adapters.
+ *
+ * @param account account for which to set backoff. Null to specify all accounts.
+ * @param userId id of the user making this request.
+ * @param providerName provider for which to set backoff. Null to specify all providers.
+ * @return true if a change occured.
*/
- public void clearAllBackoffsLocked(SyncQueue syncQueue) {
+ private boolean setBackoffLocked(Account account, int userId, String providerName,
+ long nextSyncTime, long nextDelay) {
+ boolean changed = false;
+ for (AccountInfo accountInfo : mAccounts.values()) {
+ if (account != null && !account.equals(accountInfo.accountAndUser.account)
+ && userId != accountInfo.accountAndUser.userId) {
+ continue;
+ }
+ for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) {
+ if (providerName != null
+ && !providerName.equals(authorityInfo.target.provider)) {
+ continue;
+ }
+ if (authorityInfo.backoffTime != nextSyncTime
+ || authorityInfo.backoffDelay != nextDelay) {
+ authorityInfo.backoffTime = nextSyncTime;
+ authorityInfo.backoffDelay = nextDelay;
+ changed = true;
+ }
+ }
+ }
+ return changed;
+ }
+
+ public void clearAllBackoffs(SyncQueue syncQueue) {
boolean changed = false;
synchronized (mAuthorities) {
- for (AccountInfo accountInfo : mAccounts.values()) {
- for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) {
- if (authorityInfo.backoffTime != NOT_IN_BACKOFF_MODE
- || authorityInfo.backoffDelay != NOT_IN_BACKOFF_MODE) {
- if (DEBUG) {
- Log.v(TAG, "clearAllBackoffs:"
- + " authority:" + authorityInfo.authority
- + " account:" + accountInfo.accountAndUser.account.name
- + " user:" + accountInfo.accountAndUser.userId
- + " backoffTime was: " + authorityInfo.backoffTime
- + " backoffDelay was: " + authorityInfo.backoffDelay);
+ synchronized (syncQueue) {
+ // Clear backoff for all sync adapters.
+ for (AccountInfo accountInfo : mAccounts.values()) {
+ for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) {
+ if (authorityInfo.backoffTime != NOT_IN_BACKOFF_MODE
+ || authorityInfo.backoffDelay != NOT_IN_BACKOFF_MODE) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "clearAllBackoffs:"
+ + " authority:" + authorityInfo.target
+ + " account:" + accountInfo.accountAndUser.account.name
+ + " user:" + accountInfo.accountAndUser.userId
+ + " backoffTime was: " + authorityInfo.backoffTime
+ + " backoffDelay was: " + authorityInfo.backoffDelay);
+ }
+ authorityInfo.backoffTime = NOT_IN_BACKOFF_MODE;
+ authorityInfo.backoffDelay = NOT_IN_BACKOFF_MODE;
+ changed = true;
}
- authorityInfo.backoffTime = NOT_IN_BACKOFF_MODE;
- authorityInfo.backoffDelay = NOT_IN_BACKOFF_MODE;
- syncQueue.onBackoffChanged(accountInfo.accountAndUser.account,
- accountInfo.accountAndUser.userId, authorityInfo.authority, 0);
- changed = true;
}
}
+ // Clear backoff for all sync services.
+ for (ComponentName service : mServices.keySet()) {
+ SparseArray<AuthorityInfo> aInfos = mServices.get(service);
+ for (int i = 0; i < aInfos.size(); i++) {
+ AuthorityInfo authorityInfo = aInfos.valueAt(i);
+ if (authorityInfo.backoffTime != NOT_IN_BACKOFF_MODE
+ || authorityInfo.backoffDelay != NOT_IN_BACKOFF_MODE) {
+ authorityInfo.backoffTime = NOT_IN_BACKOFF_MODE;
+ authorityInfo.backoffDelay = NOT_IN_BACKOFF_MODE;
+ }
+ }
+ }
+ syncQueue.clearBackoffs();
}
}
@@ -745,142 +878,157 @@ public class SyncStorageEngine extends Handler {
}
}
- public void setDelayUntilTime(Account account, int userId, String providerName,
- long delayUntil) {
- if (DEBUG) {
- Log.v(TAG, "setDelayUntil: " + account + ", provider " + providerName
- + ", user " + userId + " -> delayUntil " + delayUntil);
- }
+ public long getDelayUntilTime(EndPoint info) {
synchronized (mAuthorities) {
- AuthorityInfo authority = getOrCreateAuthorityLocked(
- account, userId, providerName, -1 /* ident */, true);
- if (authority.delayUntil == delayUntil) {
- return;
+ AuthorityInfo authority = getAuthorityLocked(info, "getDelayUntil");
+ if (authority == null) {
+ return 0;
}
- authority.delayUntil = delayUntil;
+ return authority.delayUntil;
}
-
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
}
- public long getDelayUntilTime(Account account, int userId, String providerName) {
+ public void setDelayUntilTime(EndPoint info, long delayUntil) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "setDelayUntil: " + info
+ + " -> delayUntil " + delayUntil);
+ }
synchronized (mAuthorities) {
- AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
- "getDelayUntil");
- if (authority == null) {
- return 0;
+ AuthorityInfo authority = getOrCreateAuthorityLocked(info, -1, true);
+ if (authority.delayUntil == delayUntil) {
+ return;
}
- return authority.delayUntil;
+ authority.delayUntil = delayUntil;
}
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
}
- private void updateOrRemovePeriodicSync(PeriodicSync toUpdate, int userId, boolean add) {
- if (DEBUG) {
- Log.v(TAG, "addOrRemovePeriodicSync: " + toUpdate.account + ", user " + userId
- + ", provider " + toUpdate.authority
- + " -> period " + toUpdate.period + ", extras " + toUpdate.extras);
+ public void updateOrAddPeriodicSync(EndPoint info, long period, long flextime, Bundle extras) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "addPeriodicSync: " + info
+ + " -> period " + period + ", flex " + flextime + ", extras "
+ + extras.toString());
}
synchronized (mAuthorities) {
- if (toUpdate.period <= 0 && add) {
- Log.e(TAG, "period < 0, should never happen in updateOrRemovePeriodicSync: add-"
- + add);
+ if (period <= 0) {
+ Log.e(TAG, "period < 0, should never happen in updateOrAddPeriodicSync");
}
- if (toUpdate.extras == null) {
- Log.e(TAG, "null extras, should never happen in updateOrRemovePeriodicSync: add-"
- + add);
+ if (extras == null) {
+ Log.e(TAG, "null extras, should never happen in updateOrAddPeriodicSync:");
}
try {
+ PeriodicSync toUpdate;
+ if (info.target_provider) {
+ toUpdate = new PeriodicSync(info.account,
+ info.provider,
+ extras,
+ period,
+ flextime);
+ } else {
+ toUpdate = new PeriodicSync(info.service,
+ extras,
+ period,
+ flextime);
+ }
AuthorityInfo authority =
- getOrCreateAuthorityLocked(toUpdate.account, userId, toUpdate.authority,
- -1, false);
- if (add) {
- // add this periodic sync if an equivalent periodic doesn't already exist.
- boolean alreadyPresent = false;
- for (int i = 0, N = authority.periodicSyncs.size(); i < N; i++) {
- PeriodicSync syncInfo = authority.periodicSyncs.get(i);
- if (PeriodicSync.syncExtrasEquals(
- toUpdate.extras,
- syncInfo.extras)) {
- if (toUpdate.period == syncInfo.period &&
- toUpdate.flexTime == syncInfo.flexTime) {
- // Absolutely the same.
- return;
- }
- authority.periodicSyncs.set(i, new PeriodicSync(toUpdate));
- alreadyPresent = true;
- break;
+ getOrCreateAuthorityLocked(info, -1, false);
+ // add this periodic sync if an equivalent periodic doesn't already exist.
+ boolean alreadyPresent = false;
+ for (int i = 0, N = authority.periodicSyncs.size(); i < N; i++) {
+ PeriodicSync syncInfo = authority.periodicSyncs.get(i);
+ if (SyncManager.syncExtrasEquals(syncInfo.extras,
+ extras,
+ true /* includeSyncSettings*/)) {
+ if (period == syncInfo.period &&
+ flextime == syncInfo.flexTime) {
+ // Absolutely the same.
+ return;
}
+ authority.periodicSyncs.set(i, toUpdate);
+ alreadyPresent = true;
+ break;
}
- // If we added an entry to the periodicSyncs array also add an entry to
- // the periodic syncs status to correspond to it.
- if (!alreadyPresent) {
- authority.periodicSyncs.add(new PeriodicSync(toUpdate));
- SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
- status.setPeriodicSyncTime(authority.periodicSyncs.size() - 1, 0L);
- }
- } else {
- // Remove any periodic syncs that match the authority and extras.
- SyncStatusInfo status = mSyncStatus.get(authority.ident);
- boolean changed = false;
- Iterator<PeriodicSync> iterator = authority.periodicSyncs.iterator();
- int i = 0;
- while (iterator.hasNext()) {
- PeriodicSync syncInfo = iterator.next();
- if (PeriodicSync.syncExtrasEquals(syncInfo.extras, toUpdate.extras)) {
- iterator.remove();
- changed = true;
- // If we removed an entry from the periodicSyncs array also
- // remove the corresponding entry from the status
- if (status != null) {
- status.removePeriodicSyncTime(i);
- } else {
- Log.e(TAG, "Tried removing sync status on remove periodic sync but"
- + "did not find it.");
- }
+ }
+ // If we added an entry to the periodicSyncs array also add an entry to
+ // the periodic syncs status to correspond to it.
+ if (!alreadyPresent) {
+ authority.periodicSyncs.add(toUpdate);
+ SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
+ // A new periodic sync is initialised as already having been run.
+ status.setPeriodicSyncTime(
+ authority.periodicSyncs.size() - 1,
+ System.currentTimeMillis());
+ }
+ } finally {
+ writeAccountInfoLocked();
+ writeStatusLocked();
+ }
+ }
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+ }
+
+ public void removePeriodicSync(EndPoint info, Bundle extras) {
+ synchronized(mAuthorities) {
+ try {
+ AuthorityInfo authority =
+ getOrCreateAuthorityLocked(info, -1, false);
+ // Remove any periodic syncs that match the target and extras.
+ SyncStatusInfo status = mSyncStatus.get(authority.ident);
+ boolean changed = false;
+ Iterator<PeriodicSync> iterator = authority.periodicSyncs.iterator();
+ int i = 0;
+ while (iterator.hasNext()) {
+ PeriodicSync syncInfo = iterator.next();
+ if (SyncManager.syncExtrasEquals(syncInfo.extras,
+ extras,
+ true /* includeSyncSettings */)) {
+ iterator.remove();
+ changed = true;
+ // If we removed an entry from the periodicSyncs array also
+ // remove the corresponding entry from the status
+ if (status != null) {
+ status.removePeriodicSyncTime(i);
} else {
- i++;
+ Log.e(TAG, "Tried removing sync status on remove periodic sync but"
+ + " did not find it.");
}
+ } else {
+ i++;
}
- if (!changed) {
- return;
- }
+ }
+ if (!changed) {
+ return;
}
} finally {
writeAccountInfoLocked();
writeStatusLocked();
}
}
-
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
}
- public void addPeriodicSync(PeriodicSync toAdd, int userId) {
- updateOrRemovePeriodicSync(toAdd, userId, true /* add */);
- }
-
- public void removePeriodicSync(PeriodicSync toRemove, int userId) {
- updateOrRemovePeriodicSync(toRemove, userId, false /* remove */);
- }
-
- public List<PeriodicSync> getPeriodicSyncs(Account account, int userId, String providerName) {
- ArrayList<PeriodicSync> syncs = new ArrayList<PeriodicSync>();
+ /**
+ * @return list of periodic syncs for a target. Never null. If no such syncs exist, returns an
+ * empty list.
+ */
+ public List<PeriodicSync> getPeriodicSyncs(EndPoint info) {
synchronized (mAuthorities) {
- AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
- "getPeriodicSyncs");
- if (authority != null) {
- for (PeriodicSync item : authority.periodicSyncs) {
+ AuthorityInfo authorityInfo = getAuthorityLocked(info, "getPeriodicSyncs");
+ ArrayList<PeriodicSync> syncs = new ArrayList<PeriodicSync>();
+ if (authorityInfo != null) {
+ for (PeriodicSync item : authorityInfo.periodicSyncs) {
// Copy and send out. Necessary for thread-safety although it's parceled.
syncs.add(new PeriodicSync(item));
}
}
+ return syncs;
}
- return syncs;
}
public void setMasterSyncAutomatically(boolean flag, int userId) {
synchronized (mAuthorities) {
Boolean auto = mMasterSyncAutomatically.get(userId);
- if (auto != null && (boolean) auto == flag) {
+ if (auto != null && auto.equals(flag)) {
return;
}
mMasterSyncAutomatically.put(userId, flag);
@@ -901,12 +1049,6 @@ public class SyncStorageEngine extends Handler {
}
}
- public void removeAuthority(Account account, int userId, String authority) {
- synchronized (mAuthorities) {
- removeAuthorityLocked(account, userId, authority, true /* doWrite */);
- }
- }
-
public AuthorityInfo getAuthority(int authorityId) {
synchronized (mAuthorities) {
return mAuthorities.get(authorityId);
@@ -914,72 +1056,60 @@ public class SyncStorageEngine extends Handler {
}
/**
- * Returns true if there is currently a sync operation for the given
- * account or authority actively being processed.
+ * Returns true if there is currently a sync operation being actively processed for the given
+ * target.
*/
- public boolean isSyncActive(Account account, int userId, String authority) {
+ public boolean isSyncActive(EndPoint info) {
synchronized (mAuthorities) {
- for (SyncInfo syncInfo : getCurrentSyncs(userId)) {
+ for (SyncInfo syncInfo : getCurrentSyncs(info.userId)) {
AuthorityInfo ainfo = getAuthority(syncInfo.authorityId);
- if (ainfo != null && ainfo.account.equals(account)
- && ainfo.authority.equals(authority)
- && ainfo.userId == userId) {
+ if (ainfo != null && ainfo.target.matchesSpec(info)) {
return true;
}
}
}
-
return false;
}
- public PendingOperation insertIntoPending(PendingOperation op) {
+ public PendingOperation insertIntoPending(SyncOperation op) {
+ PendingOperation pop;
synchronized (mAuthorities) {
- if (DEBUG) {
- Log.v(TAG, "insertIntoPending: account=" + op.account
- + " user=" + op.userId
- + " auth=" + op.authority
- + " src=" + op.syncSource
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "insertIntoPending: authority=" + op.target
+ " extras=" + op.extras);
}
-
- AuthorityInfo authority = getOrCreateAuthorityLocked(op.account, op.userId,
- op.authority,
- -1 /* desired identifier */,
- true /* write accounts to storage */);
+ final EndPoint info = op.target;
+ AuthorityInfo authority =
+ getOrCreateAuthorityLocked(info,
+ -1 /* desired identifier */,
+ true /* write accounts to storage */);
if (authority == null) {
return null;
}
- op = new PendingOperation(op);
- op.authorityId = authority.ident;
- mPendingOperations.add(op);
- appendPendingOperationLocked(op);
+ pop = new PendingOperation(authority, op.reason, op.syncSource, op.extras,
+ op.expedited);
+ mPendingOperations.add(pop);
+ appendPendingOperationLocked(pop);
SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
status.pending = true;
}
-
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
- return op;
+ return pop;
}
/**
* Remove from list of pending operations. If successful, search through list for matching
- * authorities. If there are no more pending syncs for the same authority/account/userid,
- * update the SyncStatusInfo for that authority(authority here is the internal representation
- * of a 'sync operation'.
- * @param op
- * @return
+ * authorities. If there are no more pending syncs for the same target,
+ * update the SyncStatusInfo for that target.
+ * @param op Pending op to delete.
*/
public boolean deleteFromPending(PendingOperation op) {
boolean res = false;
synchronized (mAuthorities) {
- if (DEBUG) {
- Log.v(TAG, "deleteFromPending: account=" + op.account
- + " user=" + op.userId
- + " auth=" + op.authority
- + " src=" + op.syncSource
- + " extras=" + op.extras);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "deleteFromPending: account=" + op.toString());
}
if (mPendingOperations.remove(op)) {
if (mPendingOperations.size() == 0
@@ -989,30 +1119,27 @@ public class SyncStorageEngine extends Handler {
} else {
mNumPendingFinished++;
}
-
- AuthorityInfo authority = getAuthorityLocked(op.account, op.userId, op.authority,
- "deleteFromPending");
+ AuthorityInfo authority = getAuthorityLocked(op.target, "deleteFromPending");
if (authority != null) {
- if (DEBUG) Log.v(TAG, "removing - " + authority.toString());
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "removing - " + authority.toString());
+ }
final int N = mPendingOperations.size();
boolean morePending = false;
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
PendingOperation cur = mPendingOperations.get(i);
- if (cur.account.equals(op.account)
- && cur.authority.equals(op.authority)
- && cur.userId == op.userId) {
+ if (cur.equals(op)) {
morePending = true;
break;
}
}
if (!morePending) {
- if (DEBUG) Log.v(TAG, "no more pending!");
+ if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "no more pending!");
SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
status.pending = false;
}
}
-
res = true;
}
}
@@ -1047,7 +1174,9 @@ public class SyncStorageEngine extends Handler {
*/
public void doDatabaseCleanup(Account[] accounts, int userId) {
synchronized (mAuthorities) {
- if (DEBUG) Log.v(TAG, "Updating for new accounts...");
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Updating for new accounts...");
+ }
SparseArray<AuthorityInfo> removing = new SparseArray<AuthorityInfo>();
Iterator<AccountInfo> accIt = mAccounts.values().iterator();
while (accIt.hasNext()) {
@@ -1055,7 +1184,7 @@ public class SyncStorageEngine extends Handler {
if (!ArrayUtils.contains(accounts, acc.accountAndUser.account)
&& acc.accountAndUser.userId == userId) {
// This account no longer exists...
- if (DEBUG) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Account removed: " + acc.accountAndUser);
}
for (AuthorityInfo auth : acc.authorities.values()) {
@@ -1102,25 +1231,25 @@ public class SyncStorageEngine extends Handler {
public SyncInfo addActiveSync(SyncManager.ActiveSyncContext activeSyncContext) {
final SyncInfo syncInfo;
synchronized (mAuthorities) {
- if (DEBUG) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "setActiveSync: account="
- + activeSyncContext.mSyncOperation.account
- + " auth=" + activeSyncContext.mSyncOperation.authority
+ + " auth=" + activeSyncContext.mSyncOperation.target
+ " src=" + activeSyncContext.mSyncOperation.syncSource
+ " extras=" + activeSyncContext.mSyncOperation.extras);
}
- AuthorityInfo authority = getOrCreateAuthorityLocked(
- activeSyncContext.mSyncOperation.account,
- activeSyncContext.mSyncOperation.userId,
- activeSyncContext.mSyncOperation.authority,
- -1 /* assign a new identifier if creating a new authority */,
+ final EndPoint info = activeSyncContext.mSyncOperation.target;
+ AuthorityInfo authorityInfo = getOrCreateAuthorityLocked(
+ info,
+ -1 /* assign a new identifier if creating a new target */,
true /* write to storage if this results in a change */);
- syncInfo = new SyncInfo(authority.ident,
- authority.account, authority.authority,
+ syncInfo = new SyncInfo(
+ authorityInfo.ident,
+ authorityInfo.target.account,
+ authorityInfo.target.provider,
+ authorityInfo.target.service,
activeSyncContext.mStartTime);
- getCurrentSyncs(authority.userId).add(syncInfo);
+ getCurrentSyncs(authorityInfo.target.userId).add(syncInfo);
}
-
reportActiveChange();
return syncInfo;
}
@@ -1130,10 +1259,11 @@ public class SyncStorageEngine extends Handler {
*/
public void removeActiveSync(SyncInfo syncInfo, int userId) {
synchronized (mAuthorities) {
- if (DEBUG) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "removeActiveSync: account=" + syncInfo.account
+ " user=" + userId
- + " auth=" + syncInfo.authority);
+ + " auth=" + syncInfo.authority
+ + " service=" + syncInfo.service);
}
getCurrentSyncs(userId).remove(syncInfo);
}
@@ -1149,37 +1279,34 @@ public class SyncStorageEngine extends Handler {
}
/**
- * Note that sync has started for the given account and authority.
+ * Note that sync has started for the given operation.
*/
- public long insertStartSyncEvent(Account accountName, int userId, int reason,
- String authorityName, long now, int source, boolean initialization, Bundle extras) {
+ public long insertStartSyncEvent(SyncOperation op, long now) {
long id;
synchronized (mAuthorities) {
- if (DEBUG) {
- Log.v(TAG, "insertStartSyncEvent: account=" + accountName + "user=" + userId
- + " auth=" + authorityName + " source=" + source);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "insertStartSyncEvent: " + op);
}
- AuthorityInfo authority = getAuthorityLocked(accountName, userId, authorityName,
- "insertStartSyncEvent");
+ AuthorityInfo authority = getAuthorityLocked(op.target, "insertStartSyncEvent");
if (authority == null) {
return -1;
}
SyncHistoryItem item = new SyncHistoryItem();
- item.initialization = initialization;
+ item.initialization = op.isInitialization();
item.authorityId = authority.ident;
item.historyId = mNextHistoryId++;
if (mNextHistoryId < 0) mNextHistoryId = 0;
item.eventTime = now;
- item.source = source;
- item.reason = reason;
- item.extras = extras;
+ item.source = op.syncSource;
+ item.reason = op.reason;
+ item.extras = op.extras;
item.event = EVENT_START;
mSyncHistory.add(0, item);
while (mSyncHistory.size() > MAX_HISTORY) {
mSyncHistory.remove(mSyncHistory.size()-1);
}
id = item.historyId;
- if (DEBUG) Log.v(TAG, "returning historyId " + id);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "returning historyId " + id);
}
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
@@ -1189,7 +1316,7 @@ public class SyncStorageEngine extends Handler {
public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage,
long downstreamActivity, long upstreamActivity) {
synchronized (mAuthorities) {
- if (DEBUG) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "stopSyncEvent: historyId=" + historyId);
}
SyncHistoryItem item = null;
@@ -1346,13 +1473,12 @@ public class SyncStorageEngine extends Handler {
}
/**
- * Return a copy of the specified authority with the corresponding sync status
+ * Return a copy of the specified target with the corresponding sync status
*/
- public Pair<AuthorityInfo, SyncStatusInfo> getCopyOfAuthorityWithSyncStatus(
- Account account, int userId, String authority) {
+ public Pair<AuthorityInfo, SyncStatusInfo> getCopyOfAuthorityWithSyncStatus(EndPoint info) {
synchronized (mAuthorities) {
- AuthorityInfo authorityInfo = getOrCreateAuthorityLocked(account, userId, authority,
- -1 /* assign a new identifier if creating a new authority */,
+ AuthorityInfo authorityInfo = getOrCreateAuthorityLocked(info,
+ -1 /* assign a new identifier if creating a new target */,
true /* write to storage if this results in a change */);
return createCopyPairOfAuthorityWithSyncStatusLocked(authorityInfo);
}
@@ -1373,26 +1499,24 @@ public class SyncStorageEngine extends Handler {
}
/**
- * Returns the status that matches the authority and account.
+ * Returns the status that matches the target.
*
- * @param account the account we want to check
- * @param authority the authority whose row should be selected
- * @return the SyncStatusInfo for the authority or null if none found.
+ * @param info the endpoint target we are querying status info for.
+ * @return the SyncStatusInfo for the endpoint.
*/
- public SyncStatusInfo getStatusByAccountAndAuthority(Account account, int userId,
- String authority) {
- if (account == null || authority == null) {
- return null;
+ public SyncStatusInfo getStatusByAuthority(EndPoint info) {
+ if (info.target_provider && (info.account == null || info.provider == null)) {
+ return null;
+ } else if (info.target_service && info.service == null) {
+ return null;
}
synchronized (mAuthorities) {
final int N = mSyncStatus.size();
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
SyncStatusInfo cur = mSyncStatus.valueAt(i);
AuthorityInfo ainfo = mAuthorities.get(cur.authorityId);
-
- if (ainfo != null && ainfo.authority.equals(authority)
- && ainfo.userId == userId
- && account.equals(ainfo.account)) {
+ if (ainfo != null
+ && ainfo.target.matchesSpec(info)) {
return cur;
}
}
@@ -1400,25 +1524,20 @@ public class SyncStorageEngine extends Handler {
}
}
- /**
- * Return true if the pending status is true of any matching authorities.
- */
- public boolean isSyncPending(Account account, int userId, String authority) {
+ /** Return true if the pending status is true of any matching authorities. */
+ public boolean isSyncPending(EndPoint info) {
synchronized (mAuthorities) {
final int N = mSyncStatus.size();
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
SyncStatusInfo cur = mSyncStatus.valueAt(i);
AuthorityInfo ainfo = mAuthorities.get(cur.authorityId);
if (ainfo == null) {
continue;
}
- if (userId != ainfo.userId) {
+ if (!ainfo.target.matchesSpec(info)) {
continue;
}
- if (account != null && !ainfo.account.equals(account)) {
- continue;
- }
- if (ainfo.authority.equals(authority) && cur.pending) {
+ if (cur.pending) {
return true;
}
}
@@ -1474,128 +1593,133 @@ public class SyncStorageEngine extends Handler {
}
/**
- * Retrieve an authority, returning null if one does not exist.
+ * Retrieve a target's full info, returning null if one does not exist.
*
- * @param accountName The name of the account for the authority.
- * @param authorityName The name of the authority itself.
+ * @param info info of the target to look up.
* @param tag If non-null, this will be used in a log message if the
- * requested authority does not exist.
+ * requested target does not exist.
*/
- private AuthorityInfo getAuthorityLocked(Account accountName, int userId, String authorityName,
- String tag) {
- AccountAndUser au = new AccountAndUser(accountName, userId);
- AccountInfo accountInfo = mAccounts.get(au);
- if (accountInfo == null) {
- if (tag != null) {
- if (DEBUG) {
- Log.v(TAG, tag + ": unknown account " + au);
+ private AuthorityInfo getAuthorityLocked(EndPoint info, String tag) {
+ if (info.target_service) {
+ SparseArray<AuthorityInfo> aInfo = mServices.get(info.service);
+ AuthorityInfo authority = null;
+ if (aInfo != null) {
+ authority = aInfo.get(info.userId);
+ }
+ if (authority == null) {
+ if (tag != null) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, tag + " No authority info found for " + info.service + " for"
+ + " user " + info.userId);
+ }
}
+ return null;
}
- return null;
- }
- AuthorityInfo authority = accountInfo.authorities.get(authorityName);
- if (authority == null) {
- if (tag != null) {
- if (DEBUG) {
- Log.v(TAG, tag + ": unknown authority " + authorityName);
+ return authority;
+ } else if (info.target_provider){
+ AccountAndUser au = new AccountAndUser(info.account, info.userId);
+ AccountInfo accountInfo = mAccounts.get(au);
+ if (accountInfo == null) {
+ if (tag != null) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, tag + ": unknown account " + au);
+ }
}
+ return null;
}
- return null;
- }
-
- return authority;
- }
-
- /**
- * Retrieve an authority, returning null if one does not exist.
- *
- * @param service The service name used for this sync.
- * @param userId The user for whom this sync is scheduled.
- * @param tag If non-null, this will be used in a log message if the
- * requested authority does not exist.
- */
- private AuthorityInfo getAuthorityLocked(ComponentName service, int userId, String tag) {
- AuthorityInfo authority = mServices.get(service).get(userId);
- if (authority == null) {
- if (tag != null) {
- if (DEBUG) {
- Log.v(TAG, tag + " No authority info found for " + service + " for user "
- + userId);
+ AuthorityInfo authority = accountInfo.authorities.get(info.provider);
+ if (authority == null) {
+ if (tag != null) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, tag + ": unknown provider " + info.provider);
+ }
}
+ return null;
}
+ return authority;
+ } else {
+ Log.e(TAG, tag + " Authority : " + info + ", invalid target");
return null;
}
- return authority;
}
/**
- * @param cname identifier for the service.
- * @param userId for the syncs corresponding to this authority.
- * @param ident unique identifier for authority. -1 for none.
+ * @param info info identifying target.
+ * @param ident unique identifier for target. -1 for none.
* @param doWrite if true, update the accounts.xml file on the disk.
- * @return the authority that corresponds to the provided sync service, creating it if none
+ * @return the authority that corresponds to the provided sync target, creating it if none
* exists.
*/
- private AuthorityInfo getOrCreateAuthorityLocked(ComponentName cname, int userId, int ident,
- boolean doWrite) {
- SparseArray<AuthorityInfo> aInfo = mServices.get(cname);
- if (aInfo == null) {
- aInfo = new SparseArray<AuthorityInfo>();
- mServices.put(cname, aInfo);
- }
- AuthorityInfo authority = aInfo.get(userId);
- if (authority == null) {
- if (ident < 0) {
- ident = mNextAuthorityId;
- mNextAuthorityId++;
- doWrite = true;
- }
- if (DEBUG) {
- Log.v(TAG, "created a new AuthorityInfo for " + cname.getPackageName()
- + ", " + cname.getClassName()
- + ", user: " + userId);
- }
- authority = new AuthorityInfo(cname, userId, ident);
- aInfo.put(userId, authority);
- mAuthorities.put(ident, authority);
- if (doWrite) {
- writeAccountInfoLocked();
+ private AuthorityInfo getOrCreateAuthorityLocked(EndPoint info, int ident, boolean doWrite) {
+ AuthorityInfo authority = null;
+ if (info.target_service) {
+ SparseArray<AuthorityInfo> aInfo = mServices.get(info.service);
+ if (aInfo == null) {
+ aInfo = new SparseArray<AuthorityInfo>();
+ mServices.put(info.service, aInfo);
+ }
+ authority = aInfo.get(info.userId);
+ if (authority == null) {
+ authority = createAuthorityLocked(info, ident, doWrite);
+ aInfo.put(info.userId, authority);
+ }
+ } else if (info.target_provider) {
+ AccountAndUser au = new AccountAndUser(info.account, info.userId);
+ AccountInfo account = mAccounts.get(au);
+ if (account == null) {
+ account = new AccountInfo(au);
+ mAccounts.put(au, account);
+ }
+ authority = account.authorities.get(info.provider);
+ if (authority == null) {
+ authority = createAuthorityLocked(info, ident, doWrite);
+ account.authorities.put(info.provider, authority);
}
}
return authority;
}
- private AuthorityInfo getOrCreateAuthorityLocked(Account accountName, int userId,
- String authorityName, int ident, boolean doWrite) {
- AccountAndUser au = new AccountAndUser(accountName, userId);
- AccountInfo account = mAccounts.get(au);
- if (account == null) {
- account = new AccountInfo(au);
- mAccounts.put(au, account);
- }
- AuthorityInfo authority = account.authorities.get(authorityName);
- if (authority == null) {
- if (ident < 0) {
- ident = mNextAuthorityId;
- mNextAuthorityId++;
- doWrite = true;
- }
- if (DEBUG) {
- Log.v(TAG, "created a new AuthorityInfo for " + accountName
- + ", user " + userId
- + ", provider " + authorityName);
- }
- authority = new AuthorityInfo(accountName, userId, authorityName, ident);
- account.authorities.put(authorityName, authority);
- mAuthorities.put(ident, authority);
- if (doWrite) {
- writeAccountInfoLocked();
- }
+ private AuthorityInfo createAuthorityLocked(EndPoint info, int ident, boolean doWrite) {
+ AuthorityInfo authority;
+ if (ident < 0) {
+ ident = mNextAuthorityId;
+ mNextAuthorityId++;
+ doWrite = true;
+ }
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "created a new AuthorityInfo for " + info);
+ }
+ authority = new AuthorityInfo(info, ident);
+ mAuthorities.put(ident, authority);
+ if (doWrite) {
+ writeAccountInfoLocked();
}
-
return authority;
}
+ public void removeAuthority(EndPoint info) {
+ synchronized (mAuthorities) {
+ if (info.target_provider) {
+ removeAuthorityLocked(info.account, info.userId, info.provider, true /* doWrite */);
+ } else {
+ SparseArray<AuthorityInfo> aInfos = mServices.get(info.service);
+ if (aInfos != null) {
+ AuthorityInfo authorityInfo = aInfos.get(info.userId);
+ if (authorityInfo != null) {
+ mAuthorities.remove(authorityInfo.ident);
+ aInfos.delete(info.userId);
+ writeAccountInfoLocked();
+ }
+ }
+
+ }
+ }
+ }
+
+ /**
+ * Remove an authority associated with a provider. Needs to be a standalone function for
+ * backward compatibility.
+ */
private void removeAuthorityLocked(Account account, int userId, String authorityName,
boolean doWrite) {
AccountInfo accountInfo = mAccounts.get(new AccountAndUser(account, userId));
@@ -1612,10 +1736,9 @@ public class SyncStorageEngine extends Handler {
/**
* Updates (in a synchronized way) the periodic sync time of the specified
- * authority id and target periodic sync
+ * target id and target periodic sync
*/
- public void setPeriodicSyncTime(
- int authorityId, PeriodicSync targetPeriodicSync, long when) {
+ public void setPeriodicSyncTime(int authorityId, PeriodicSync targetPeriodicSync, long when) {
boolean found = false;
final AuthorityInfo authorityInfo;
synchronized (mAuthorities) {
@@ -1631,7 +1754,7 @@ public class SyncStorageEngine extends Handler {
}
if (!found) {
Log.w(TAG, "Ignoring setPeriodicSyncTime request for a sync that does not exist. " +
- "Authority: " + authorityInfo.authority);
+ "Authority: " + authorityInfo.target);
}
}
@@ -1692,7 +1815,7 @@ public class SyncStorageEngine extends Handler {
try {
fis = mAccountInfoFile.openRead();
if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG, "Reading " + mAccountInfoFile.getBaseFile());
+ Log.v(TAG_FILE, "Reading " + mAccountInfoFile.getBaseFile());
}
XmlPullParser parser = Xml.newPullParser();
parser.setInput(fis, null);
@@ -1806,10 +1929,14 @@ public class SyncStorageEngine extends Handler {
ArrayList<AuthorityInfo> authoritiesToRemove = new ArrayList<AuthorityInfo>();
final int N = mAuthorities.size();
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
AuthorityInfo authority = mAuthorities.valueAt(i);
+ // skip this authority if it doesn't target a provider
+ if (authority.target.target_service) {
+ continue;
+ }
// skip this authority if it isn't one of the renamed ones
- final String newAuthorityName = sAuthorityRenames.get(authority.authority);
+ final String newAuthorityName = sAuthorityRenames.get(authority.target.provider);
if (newAuthorityName == null) {
continue;
}
@@ -1825,20 +1952,26 @@ public class SyncStorageEngine extends Handler {
}
// if we already have a record of this new authority then don't copy over the settings
- if (getAuthorityLocked(authority.account, authority.userId, newAuthorityName, "cleanup")
- != null) {
+ EndPoint newInfo =
+ new EndPoint(authority.target.account,
+ newAuthorityName,
+ authority.target.userId);
+ if (getAuthorityLocked(newInfo, "cleanup") != null) {
continue;
}
- AuthorityInfo newAuthority = getOrCreateAuthorityLocked(authority.account,
- authority.userId, newAuthorityName, -1 /* ident */, false /* doWrite */);
+ AuthorityInfo newAuthority =
+ getOrCreateAuthorityLocked(newInfo, -1 /* ident */, false /* doWrite */);
newAuthority.enabled = true;
writeNeeded = true;
}
for (AuthorityInfo authorityInfo : authoritiesToRemove) {
- removeAuthorityLocked(authorityInfo.account, authorityInfo.userId,
- authorityInfo.authority, false /* doWrite */);
+ removeAuthorityLocked(
+ authorityInfo.target.account,
+ authorityInfo.target.userId,
+ authorityInfo.target.provider,
+ false /* doWrite */);
writeNeeded = true;
}
@@ -1880,30 +2013,37 @@ public class SyncStorageEngine extends Handler {
String packageName = parser.getAttributeValue(null, "package");
String className = parser.getAttributeValue(null, "class");
int userId = user == null ? 0 : Integer.parseInt(user);
- if (accountType == null) {
+ if (accountType == null && packageName == null) {
accountType = "com.google";
syncable = "unknown";
}
authority = mAuthorities.get(id);
if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG, "Adding authority: account="
- + accountName + " auth=" + authorityName
+ Log.v(TAG_FILE, "Adding authority:"
+ + " account=" + accountName
+ + " accountType=" + accountType
+ + " auth=" + authorityName
+ + " package=" + packageName
+ + " class=" + className
+ " user=" + userId
+ " enabled=" + enabled
+ " syncable=" + syncable);
}
if (authority == null) {
if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG, "Creating entry");
+ Log.v(TAG_FILE, "Creating authority entry");
}
- if (accountName != null && accountType != null) {
- authority = getOrCreateAuthorityLocked(
- new Account(accountName, accountType), userId, authorityName, id,
- false);
+ EndPoint info;
+ if (accountName != null && authorityName != null) {
+ info = new EndPoint(
+ new Account(accountName, accountType),
+ authorityName, userId);
} else {
- authority = getOrCreateAuthorityLocked(
- new ComponentName(packageName, className), userId, id, false);
+ info = new EndPoint(
+ new ComponentName(packageName, className),
+ userId);
}
+ authority = getOrCreateAuthorityLocked(info, id, false);
// If the version is 0 then we are upgrading from a file format that did not
// know about periodic syncs. In that case don't clear the list since we
// want the default, which is a daily periodic sync.
@@ -1934,7 +2074,7 @@ public class SyncStorageEngine extends Handler {
/**
* Parse a periodic sync from accounts.xml. Sets the bundle to be empty.
*/
- private PeriodicSync parsePeriodicSync(XmlPullParser parser, AuthorityInfo authority) {
+ private PeriodicSync parsePeriodicSync(XmlPullParser parser, AuthorityInfo authorityInfo) {
Bundle extras = new Bundle(); // Gets filled in later.
String periodValue = parser.getAttributeValue(null, "period");
String flexValue = parser.getAttributeValue(null, "flex");
@@ -1952,17 +2092,31 @@ public class SyncStorageEngine extends Handler {
try {
flextime = Long.parseLong(flexValue);
} catch (NumberFormatException e) {
- Log.e(TAG, "Error formatting value parsed for periodic sync flex: " + flexValue);
flextime = calculateDefaultFlexTime(period);
+ Log.e(TAG, "Error formatting value parsed for periodic sync flex: " + flexValue
+ + ", using default: "
+ + flextime);
} catch (NullPointerException expected) {
flextime = calculateDefaultFlexTime(period);
Log.d(TAG, "No flex time specified for this sync, using a default. period: "
+ period + " flex: " + flextime);
}
- final PeriodicSync periodicSync =
- new PeriodicSync(authority.account, authority.authority, extras,
+ PeriodicSync periodicSync;
+ if (authorityInfo.target.target_provider) {
+ periodicSync =
+ new PeriodicSync(authorityInfo.target.account,
+ authorityInfo.target.provider,
+ extras,
period, flextime);
- authority.periodicSyncs.add(periodicSync);
+ } else {
+ periodicSync =
+ new PeriodicSync(
+ authorityInfo.target.service,
+ extras,
+ period,
+ flextime);
+ }
+ authorityInfo.periodicSyncs.add(periodicSync);
return periodicSync;
}
@@ -2000,7 +2154,7 @@ public class SyncStorageEngine extends Handler {
*/
private void writeAccountInfoLocked() {
if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG, "Writing new " + mAccountInfoFile.getBaseFile());
+ Log.v(TAG_FILE, "Writing new " + mAccountInfoFile.getBaseFile());
}
FileOutputStream fos = null;
@@ -2030,17 +2184,18 @@ public class SyncStorageEngine extends Handler {
final int N = mAuthorities.size();
for (int i = 0; i < N; i++) {
AuthorityInfo authority = mAuthorities.valueAt(i);
+ EndPoint info = authority.target;
out.startTag(null, "authority");
out.attribute(null, "id", Integer.toString(authority.ident));
- out.attribute(null, XML_ATTR_USER, Integer.toString(authority.userId));
+ out.attribute(null, XML_ATTR_USER, Integer.toString(info.userId));
out.attribute(null, XML_ATTR_ENABLED, Boolean.toString(authority.enabled));
- if (authority.service == null) {
- out.attribute(null, "account", authority.account.name);
- out.attribute(null, "type", authority.account.type);
- out.attribute(null, "authority", authority.authority);
+ if (info.service == null) {
+ out.attribute(null, "account", info.account.name);
+ out.attribute(null, "type", info.account.type);
+ out.attribute(null, "authority", info.provider);
} else {
- out.attribute(null, "package", authority.service.getPackageName());
- out.attribute(null, "class", authority.service.getClassName());
+ out.attribute(null, "package", info.service.getPackageName());
+ out.attribute(null, "class", info.service.getClassName());
}
if (authority.syncable < 0) {
out.attribute(null, "syncable", "unknown");
@@ -2100,7 +2255,7 @@ public class SyncStorageEngine extends Handler {
// Copy in all of the status information, as well as accounts.
if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG, "Reading legacy sync accounts db");
+ Log.v(TAG_FILE, "Reading legacy sync accounts db");
}
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables("stats, status");
@@ -2134,9 +2289,13 @@ public class SyncStorageEngine extends Handler {
accountType = "com.google";
}
String authorityName = c.getString(c.getColumnIndex("authority"));
- AuthorityInfo authority = this.getOrCreateAuthorityLocked(
- new Account(accountName, accountType), 0 /* legacy is single-user */,
- authorityName, -1, false);
+ AuthorityInfo authority =
+ this.getOrCreateAuthorityLocked(
+ new EndPoint(new Account(accountName, accountType),
+ authorityName,
+ 0 /* legacy is single-user */)
+ , -1,
+ false);
if (authority != null) {
int i = mSyncStatus.size();
boolean found = false;
@@ -2188,7 +2347,7 @@ public class SyncStorageEngine extends Handler {
while (i > 0) {
i--;
AuthorityInfo authority = mAuthorities.valueAt(i);
- if (authority.authority.equals(provider)) {
+ if (authority.target.provider.equals(provider)) {
authority.enabled = value == null || Boolean.parseBoolean(value);
authority.syncable = 1;
}
@@ -2212,7 +2371,7 @@ public class SyncStorageEngine extends Handler {
*/
private void readStatusLocked() {
if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG, "Reading " + mStatusFile.getBaseFile());
+ Log.v(TAG_FILE, "Reading " + mStatusFile.getBaseFile());
}
try {
byte[] data = mStatusFile.readFully();
@@ -2226,8 +2385,7 @@ public class SyncStorageEngine extends Handler {
if (mAuthorities.indexOfKey(status.authorityId) >= 0) {
status.pending = false;
if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG, "Adding status for id "
- + status.authorityId);
+ Log.v(TAG_FILE, "Adding status for id " + status.authorityId);
}
mSyncStatus.put(status.authorityId, status);
}
@@ -2247,7 +2405,7 @@ public class SyncStorageEngine extends Handler {
*/
private void writeStatusLocked() {
if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG, "Writing new " + mStatusFile.getBaseFile());
+ Log.v(TAG_FILE, "Writing new " + mStatusFile.getBaseFile());
}
// The file is being written, so we don't need to have a scheduled
@@ -2290,6 +2448,9 @@ public class SyncStorageEngine extends Handler {
}
try {
fis = mPendingFile.openRead();
+ if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
+ Log.v(TAG_FILE, "Reading " + mPendingFile.getBaseFile());
+ }
XmlPullParser parser;
parser = Xml.newPullParser();
parser.setInput(fis, null);
@@ -2301,12 +2462,11 @@ public class SyncStorageEngine extends Handler {
}
if (eventType == XmlPullParser.END_DOCUMENT) return; // Nothing to read.
- String tagName = parser.getName();
do {
PendingOperation pop = null;
if (eventType == XmlPullParser.START_TAG) {
try {
- tagName = parser.getName();
+ String tagName = parser.getName();
if (parser.getDepth() == 1 && "op".equals(tagName)) {
// Verify version.
String versionString =
@@ -2331,18 +2491,16 @@ public class SyncStorageEngine extends Handler {
}
if (authority != null) {
pop = new PendingOperation(
- authority.account, authority.userId, reason,
- syncSource, authority.authority, new Bundle(),
- expedited);
+ authority, reason, syncSource, new Bundle(), expedited);
pop.flatExtras = null; // No longer used.
mPendingOperations.add(pop);
if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
Log.v(TAG_FILE, "Adding pending op: "
- + pop.authority
+ + pop.target
+ " src=" + pop.syncSource
+ " reason=" + pop.reason
+ " expedited=" + pop.expedited);
- }
+ }
} else {
// Skip non-existent authority.
pop = null;
@@ -2377,11 +2535,40 @@ public class SyncStorageEngine extends Handler {
}
}
+ static private byte[] flattenBundle(Bundle bundle) {
+ byte[] flatData = null;
+ Parcel parcel = Parcel.obtain();
+ try {
+ bundle.writeToParcel(parcel, 0);
+ flatData = parcel.marshall();
+ } finally {
+ parcel.recycle();
+ }
+ return flatData;
+ }
+
+ static private Bundle unflattenBundle(byte[] flatData) {
+ Bundle bundle;
+ Parcel parcel = Parcel.obtain();
+ try {
+ parcel.unmarshall(flatData, 0, flatData.length);
+ parcel.setDataPosition(0);
+ bundle = parcel.readBundle();
+ } catch (RuntimeException e) {
+ // A RuntimeException is thrown if we were unable to parse the parcel.
+ // Create an empty parcel in this case.
+ bundle = new Bundle();
+ } finally {
+ parcel.recycle();
+ }
+ return bundle;
+ }
+
+ private static final String XML_ATTR_VERSION = "version";
private static final String XML_ATTR_AUTHORITYID = "authority_id";
private static final String XML_ATTR_SOURCE = "source";
private static final String XML_ATTR_EXPEDITED = "expedited";
private static final String XML_ATTR_REASON = "reason";
- private static final String XML_ATTR_VERSION = "version";
/**
* Write all currently pending ops to the pending ops file.
@@ -2391,14 +2578,14 @@ public class SyncStorageEngine extends Handler {
FileOutputStream fos = null;
try {
if (N == 0) {
- if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG_FILE, "Truncating " + mPendingFile.getBaseFile());
+ if (Log.isLoggable(TAG_FILE, Log.VERBOSE)){
+ Log.v(TAG, "Truncating " + mPendingFile.getBaseFile());
}
mPendingFile.truncate();
return;
}
if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG_FILE, "Writing new " + mPendingFile.getBaseFile());
+ Log.v(TAG, "Writing new " + mPendingFile.getBaseFile());
}
fos = mPendingFile.startWrite();
XmlSerializer out = new FastXmlSerializer();
@@ -2407,9 +2594,9 @@ public class SyncStorageEngine extends Handler {
for (int i = 0; i < N; i++) {
PendingOperation pop = mPendingOperations.get(i);
writePendingOperationLocked(pop, out);
- }
- out.endDocument();
- mPendingFile.finishWrite(fos);
+ }
+ out.endDocument();
+ mPendingFile.finishWrite(fos);
} catch (java.io.IOException e1) {
Log.w(TAG, "Error writing pending operations", e1);
if (fos != null) {
@@ -2469,35 +2656,6 @@ public class SyncStorageEngine extends Handler {
}
}
- static private byte[] flattenBundle(Bundle bundle) {
- byte[] flatData = null;
- Parcel parcel = Parcel.obtain();
- try {
- bundle.writeToParcel(parcel, 0);
- flatData = parcel.marshall();
- } finally {
- parcel.recycle();
- }
- return flatData;
- }
-
- static private Bundle unflattenBundle(byte[] flatData) {
- Bundle bundle;
- Parcel parcel = Parcel.obtain();
- try {
- parcel.unmarshall(flatData, 0, flatData.length);
- parcel.setDataPosition(0);
- bundle = parcel.readBundle();
- } catch (RuntimeException e) {
- // A RuntimeException is thrown if we were unable to parse the parcel.
- // Create an empty parcel in this case.
- bundle = new Bundle();
- } finally {
- parcel.recycle();
- }
- return bundle;
- }
-
private void extrasToXml(XmlSerializer out, Bundle extras) throws java.io.IOException {
for (String key : extras.keySet()) {
out.startTag(null, "extra");
@@ -2530,6 +2688,24 @@ public class SyncStorageEngine extends Handler {
}
}
+ private void requestSync(AuthorityInfo authorityInfo, int reason, Bundle extras) {
+ if (android.os.Process.myUid() == android.os.Process.SYSTEM_UID
+ && mSyncRequestListener != null) {
+ mSyncRequestListener.onSyncRequest(authorityInfo.target, reason, extras);
+ } else {
+ SyncRequest.Builder req =
+ new SyncRequest.Builder()
+ .syncOnce()
+ .setExtras(extras);
+ if (authorityInfo.target.target_provider) {
+ req.setSyncAdapter(authorityInfo.target.account, authorityInfo.target.provider);
+ } else {
+ req.setSyncAdapter(authorityInfo.target.service);
+ }
+ ContentResolver.requestSync(req.build());
+ }
+ }
+
private void requestSync(Account account, int userId, int reason, String authority,
Bundle extras) {
// If this is happening in the system process, then call the syncrequest listener
@@ -2538,7 +2714,10 @@ public class SyncStorageEngine extends Handler {
// which will know which userId to apply based on the Binder id.
if (android.os.Process.myUid() == android.os.Process.SYSTEM_UID
&& mSyncRequestListener != null) {
- mSyncRequestListener.onSyncRequest(account, userId, reason, authority, extras);
+ mSyncRequestListener.onSyncRequest(
+ new EndPoint(account, authority, userId),
+ reason,
+ extras);
} else {
ContentResolver.requestSync(account, authority, extras);
}
@@ -2634,10 +2813,8 @@ public class SyncStorageEngine extends Handler {
public void dumpPendingOperations(StringBuilder sb) {
sb.append("Pending Ops: ").append(mPendingOperations.size()).append(" operation(s)\n");
for (PendingOperation pop : mPendingOperations) {
- sb.append("(" + pop.account)
- .append(", u" + pop.userId)
- .append(", " + pop.authority)
- .append(", " + pop.extras)
+ sb.append("(info: " + pop.target.toString())
+ .append(", extras: " + pop.extras)
.append(")\n");
}
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index cb8f3e2..02be477 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -26,7 +26,6 @@ import android.view.Display;
import android.view.DisplayEventReceiver;
import android.view.Surface;
import android.view.SurfaceControl;
-import android.view.SurfaceControl.PhysicalDisplayInfo;
import java.io.PrintWriter;
@@ -166,6 +165,12 @@ final class LocalDisplayAdapter extends DisplayAdapter {
if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
mInfo.rotation = Surface.ROTATION_270;
}
+
+ // For demonstration purposes, allow rotation of the external display
+ // to follow the built-in display.
+ if (SystemProperties.getBoolean("persist.demo.hdmirotates", false)) {
+ mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
+ }
}
}
return mInfo;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 7e357c0..c26c438 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -258,8 +258,7 @@ final class LogicalDisplay {
// The orientation specifies how the physical coordinate system of the display
// is rotated when the contents of the logical display are rendered.
int orientation = Surface.ROTATION_0;
- if (device == mPrimaryDisplayDevice
- && (displayDeviceInfo.flags & DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT) != 0) {
+ if ((displayDeviceInfo.flags & DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT) != 0) {
orientation = displayInfo.rotation;
}
diff --git a/services/core/java/com/android/server/firewall/IntentFirewall.java b/services/core/java/com/android/server/firewall/IntentFirewall.java
index eb7a383..62114cd 100644
--- a/services/core/java/com/android/server/firewall/IntentFirewall.java
+++ b/services/core/java/com/android/server/firewall/IntentFirewall.java
@@ -95,6 +95,7 @@ public class IntentFirewall {
CategoryFilter.FACTORY,
SenderFilter.FACTORY,
+ SenderPackageFilter.FACTORY,
SenderPermissionFilter.FACTORY,
PortFilter.FACTORY
};
diff --git a/services/core/java/com/android/server/firewall/SenderPackageFilter.java b/services/core/java/com/android/server/firewall/SenderPackageFilter.java
new file mode 100644
index 0000000..ec9b5de
--- /dev/null
+++ b/services/core/java/com/android/server/firewall/SenderPackageFilter.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.firewall;
+
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.os.RemoteException;
+import android.os.UserHandle;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+public class SenderPackageFilter implements Filter {
+ private static final String ATTR_NAME = "name";
+
+ public final String mPackageName;
+
+ public SenderPackageFilter(String packageName) {
+ mPackageName = packageName;
+ }
+
+ @Override
+ public boolean matches(IntentFirewall ifw, ComponentName resolvedComponent, Intent intent,
+ int callerUid, int callerPid, String resolvedType, int receivingUid) {
+ IPackageManager pm = AppGlobals.getPackageManager();
+
+ int packageUid = -1;
+ try {
+ packageUid = pm.getPackageUid(mPackageName, UserHandle.USER_OWNER);
+ } catch (RemoteException ex) {
+ // handled below
+ }
+
+ if (packageUid == -1) {
+ return false;
+ }
+
+ return UserHandle.isSameApp(packageUid, callerUid);
+ }
+
+ public static final FilterFactory FACTORY = new FilterFactory("sender-package") {
+ @Override
+ public Filter newFilter(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ String packageName = parser.getAttributeValue(null, ATTR_NAME);
+
+ if (packageName == null) {
+ throw new XmlPullParserException(
+ "A package name must be specified.", parser, null);
+ }
+
+ return new SenderPackageFilter(packageName);
+ }
+ };
+}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 73033e0..e49382e 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -48,6 +48,7 @@ import android.database.ContentObserver;
import android.hardware.display.DisplayViewport;
import android.hardware.input.IInputDevicesChangedListener;
import android.hardware.input.IInputManager;
+import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerInternal;
import android.hardware.input.KeyboardLayout;
@@ -382,7 +383,7 @@ public class InputManagerService extends IInputManager.Stub
public int getScanCodeState(int deviceId, int sourceMask, int scanCode) {
return nativeGetScanCodeState(mPtr, deviceId, sourceMask, scanCode);
}
-
+
/**
* Gets the current state of a switch by switch code.
* @param deviceId The input device id, or -1 to consult all devices.
@@ -417,10 +418,10 @@ public class InputManagerService extends IInputManager.Stub
throw new IllegalArgumentException("keyExists must not be null and must be at "
+ "least as large as keyCodes.");
}
-
+
return nativeHasKeys(mPtr, deviceId, sourceMask, keyCodes, keyExists);
}
-
+
/**
* Creates an input channel that will receive all input from the input dispatcher.
* @param inputChannelName The input channel name.
@@ -430,7 +431,7 @@ public class InputManagerService extends IInputManager.Stub
if (inputChannelName == null) {
throw new IllegalArgumentException("inputChannelName must not be null.");
}
-
+
InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
nativeRegisterInputChannel(mPtr, inputChannels[0], null, true);
inputChannels[0].dispose(); // don't need to retain the Java object reference
@@ -448,10 +449,10 @@ public class InputManagerService extends IInputManager.Stub
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null.");
}
-
+
nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
}
-
+
/**
* Unregisters an input channel.
* @param inputChannel The input channel to unregister.
@@ -460,7 +461,7 @@ public class InputManagerService extends IInputManager.Stub
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null.");
}
-
+
nativeUnregisterInputChannel(mPtr, inputChannel);
}
@@ -902,35 +903,62 @@ public class InputManagerService extends IInputManager.Stub
}
}
- @Override // Binder call
- public String getCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor) {
- if (inputDeviceDescriptor == null) {
- throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
+ /**
+ * Builds a layout descriptor for the vendor/product. This returns the
+ * descriptor for ids that aren't useful (such as the default 0, 0).
+ */
+ private String getLayoutDescriptor(InputDeviceIdentifier identifier) {
+ if (identifier == null || identifier.getDescriptor() == null) {
+ throw new IllegalArgumentException("identifier and descriptor must not be null");
+ }
+
+ if (identifier.getVendorId() == 0 && identifier.getProductId() == 0) {
+ return identifier.getDescriptor();
}
+ StringBuilder bob = new StringBuilder();
+ bob.append("vendor:").append(identifier.getVendorId());
+ bob.append(",product:").append(identifier.getProductId());
+ return bob.toString();
+ }
+
+ @Override // Binder call
+ public String getCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier) {
+ String key = getLayoutDescriptor(identifier);
synchronized (mDataStore) {
- return mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor);
+ String layout = null;
+ // try loading it using the layout descriptor if we have it
+ layout = mDataStore.getCurrentKeyboardLayout(key);
+ if (layout == null && !key.equals(identifier.getDescriptor())) {
+ // if it doesn't exist fall back to the device descriptor
+ layout = mDataStore.getCurrentKeyboardLayout(identifier.getDescriptor());
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "Loaded keyboard layout id for " + key + " and got "
+ + layout);
+ }
+ return layout;
}
}
@Override // Binder call
- public void setCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+ public void setCurrentKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
String keyboardLayoutDescriptor) {
if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
"setCurrentKeyboardLayoutForInputDevice()")) {
throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
}
- if (inputDeviceDescriptor == null) {
- throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
- }
if (keyboardLayoutDescriptor == null) {
throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
}
+ String key = getLayoutDescriptor(identifier);
synchronized (mDataStore) {
try {
- if (mDataStore.setCurrentKeyboardLayout(
- inputDeviceDescriptor, keyboardLayoutDescriptor)) {
+ if (mDataStore.setCurrentKeyboardLayout(key, keyboardLayoutDescriptor)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Saved keyboard layout using " + key);
+ }
mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
}
} finally {
@@ -940,36 +968,39 @@ public class InputManagerService extends IInputManager.Stub
}
@Override // Binder call
- public String[] getKeyboardLayoutsForInputDevice(String inputDeviceDescriptor) {
- if (inputDeviceDescriptor == null) {
- throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
- }
-
+ public String[] getKeyboardLayoutsForInputDevice(InputDeviceIdentifier identifier) {
+ String key = getLayoutDescriptor(identifier);
synchronized (mDataStore) {
- return mDataStore.getKeyboardLayouts(inputDeviceDescriptor);
+ String[] layouts = mDataStore.getKeyboardLayouts(key);
+ if ((layouts == null || layouts.length == 0)
+ && !key.equals(identifier.getDescriptor())) {
+ layouts = mDataStore.getKeyboardLayouts(identifier.getDescriptor());
+ }
+ return layouts;
}
}
@Override // Binder call
- public void addKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+ public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
String keyboardLayoutDescriptor) {
if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
"addKeyboardLayoutForInputDevice()")) {
throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
}
- if (inputDeviceDescriptor == null) {
- throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
- }
if (keyboardLayoutDescriptor == null) {
throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
}
+ String key = getLayoutDescriptor(identifier);
synchronized (mDataStore) {
try {
- String oldLayout = mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor);
- if (mDataStore.addKeyboardLayout(inputDeviceDescriptor, keyboardLayoutDescriptor)
+ String oldLayout = mDataStore.getCurrentKeyboardLayout(key);
+ if (oldLayout == null && !key.equals(identifier.getDescriptor())) {
+ oldLayout = mDataStore.getCurrentKeyboardLayout(identifier.getDescriptor());
+ }
+ if (mDataStore.addKeyboardLayout(key, keyboardLayoutDescriptor)
&& !Objects.equal(oldLayout,
- mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor))) {
+ mDataStore.getCurrentKeyboardLayout(key))) {
mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
}
} finally {
@@ -979,26 +1010,31 @@ public class InputManagerService extends IInputManager.Stub
}
@Override // Binder call
- public void removeKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+ public void removeKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
String keyboardLayoutDescriptor) {
if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
"removeKeyboardLayoutForInputDevice()")) {
throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
}
- if (inputDeviceDescriptor == null) {
- throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
- }
if (keyboardLayoutDescriptor == null) {
throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
}
+ String key = getLayoutDescriptor(identifier);
synchronized (mDataStore) {
try {
- String oldLayout = mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor);
- if (mDataStore.removeKeyboardLayout(inputDeviceDescriptor,
- keyboardLayoutDescriptor)
- && !Objects.equal(oldLayout,
- mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor))) {
+ String oldLayout = mDataStore.getCurrentKeyboardLayout(key);
+ if (oldLayout == null && !key.equals(identifier.getDescriptor())) {
+ oldLayout = mDataStore.getCurrentKeyboardLayout(identifier.getDescriptor());
+ }
+ boolean removed = mDataStore.removeKeyboardLayout(key, keyboardLayoutDescriptor);
+ if (!key.equals(identifier.getDescriptor())) {
+ // We need to remove from both places to ensure it is gone
+ removed |= mDataStore.removeKeyboardLayout(identifier.getDescriptor(),
+ keyboardLayoutDescriptor);
+ }
+ if (removed && !Objects.equal(oldLayout,
+ mDataStore.getCurrentKeyboardLayout(key))) {
mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
}
} finally {
@@ -1015,14 +1051,15 @@ public class InputManagerService extends IInputManager.Stub
private void handleSwitchKeyboardLayout(int deviceId, int direction) {
final InputDevice device = getInputDevice(deviceId);
if (device != null) {
- final String inputDeviceDescriptor = device.getDescriptor();
final boolean changed;
final String keyboardLayoutDescriptor;
+
+ String key = getLayoutDescriptor(device.getIdentifier());
synchronized (mDataStore) {
try {
- changed = mDataStore.switchKeyboardLayout(inputDeviceDescriptor, direction);
+ changed = mDataStore.switchKeyboardLayout(key, direction);
keyboardLayoutDescriptor = mDataStore.getCurrentKeyboardLayout(
- inputDeviceDescriptor);
+ key);
} finally {
mDataStore.saveIfNeeded();
}
@@ -1050,11 +1087,11 @@ public class InputManagerService extends IInputManager.Stub
public void setInputWindows(InputWindowHandle[] windowHandles) {
nativeSetInputWindows(mPtr, windowHandles);
}
-
+
public void setFocusedApplication(InputApplicationHandle application) {
nativeSetFocusedApplication(mPtr, application);
}
-
+
public void setInputDispatchMode(boolean enabled, boolean frozen) {
nativeSetInputDispatchMode(mPtr, enabled, frozen);
}
@@ -1435,13 +1472,12 @@ public class InputManagerService extends IInputManager.Stub
}
// Native callback.
- private String[] getKeyboardLayoutOverlay(String inputDeviceDescriptor) {
+ private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier) {
if (!mSystemReady) {
return null;
}
- String keyboardLayoutDescriptor = getCurrentKeyboardLayoutForInputDevice(
- inputDeviceDescriptor);
+ String keyboardLayoutDescriptor = getCurrentKeyboardLayoutForInputDevice(identifier);
if (keyboardLayoutDescriptor == null) {
return null;
}
diff --git a/services/core/java/com/android/server/location/ComprehensiveCountryDetector.java b/services/core/java/com/android/server/location/ComprehensiveCountryDetector.java
index 354858b..6117a9b 100644
--- a/services/core/java/com/android/server/location/ComprehensiveCountryDetector.java
+++ b/services/core/java/com/android/server/location/ComprehensiveCountryDetector.java
@@ -26,7 +26,6 @@ import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
-import android.util.Log;
import android.util.Slog;
import java.util.Locale;
diff --git a/services/core/java/com/android/server/location/GeocoderProxy.java b/services/core/java/com/android/server/location/GeocoderProxy.java
index 5d4a770..422b94b 100644
--- a/services/core/java/com/android/server/location/GeocoderProxy.java
+++ b/services/core/java/com/android/server/location/GeocoderProxy.java
@@ -22,7 +22,6 @@ import android.location.GeocoderParams;
import android.location.IGeocodeProvider;
import android.os.Handler;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
import com.android.server.ServiceWatcher;
diff --git a/services/core/java/com/android/server/location/GeofenceProxy.java b/services/core/java/com/android/server/location/GeofenceProxy.java
index bbc1f47..b886eef 100644
--- a/services/core/java/com/android/server/location/GeofenceProxy.java
+++ b/services/core/java/com/android/server/location/GeofenceProxy.java
@@ -32,8 +32,6 @@ import android.os.UserHandle;
import android.util.Log;
import com.android.server.ServiceWatcher;
-import java.util.List;
-
/**
* @hide
*/
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index 9c76c19..c6cf68f 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -791,6 +791,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
private void handleDisable() {
if (DEBUG) Log.d(TAG, "handleDisable");
+ updateClientUids(new WorkSource());
stopNavigating();
mAlarmManager.cancel(mWakeupIntent);
mAlarmManager.cancel(mTimeoutIntent);
diff --git a/services/core/java/com/android/server/location/GpsXtraDownloader.java b/services/core/java/com/android/server/location/GpsXtraDownloader.java
index e420073..9dedb35 100644
--- a/services/core/java/com/android/server/location/GpsXtraDownloader.java
+++ b/services/core/java/com/android/server/location/GpsXtraDownloader.java
@@ -25,7 +25,6 @@ import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
-import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.params.ConnRouteParams;
diff --git a/services/core/java/com/android/server/location/LocationFudger.java b/services/core/java/com/android/server/location/LocationFudger.java
index 2a68743..ae71fe3 100644
--- a/services/core/java/com/android/server/location/LocationFudger.java
+++ b/services/core/java/com/android/server/location/LocationFudger.java
@@ -22,10 +22,7 @@ import java.security.SecureRandom;
import android.content.Context;
import android.database.ContentObserver;
import android.location.Location;
-import android.location.LocationManager;
-import android.os.Bundle;
import android.os.Handler;
-import android.os.Parcelable;
import android.os.SystemClock;
import android.provider.Settings;
import android.util.Log;
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index 14db862..5eb06ed 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -18,7 +18,6 @@ package com.android.server.location;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.List;
import android.content.Context;
import android.location.LocationProvider;
diff --git a/services/core/java/com/android/server/location/LocationRequestStatistics.java b/services/core/java/com/android/server/location/LocationRequestStatistics.java
new file mode 100644
index 0000000..85231bb
--- /dev/null
+++ b/services/core/java/com/android/server/location/LocationRequestStatistics.java
@@ -0,0 +1,205 @@
+package com.android.server.location;
+
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * Holds statistics for location requests (active requests by provider).
+ *
+ * <p>Must be externally synchronized.
+ */
+public class LocationRequestStatistics {
+ private static final String TAG = "LocationStats";
+
+ // Maps package name nad provider to location request statistics.
+ public final HashMap<PackageProviderKey, PackageStatistics> statistics
+ = new HashMap<PackageProviderKey, PackageStatistics>();
+
+ /**
+ * Signals that a package has started requesting locations.
+ *
+ * @param packageName Name of package that has requested locations.
+ * @param providerName Name of provider that is requested (e.g. "gps").
+ * @param intervalMs The interval that is requested in ms.
+ */
+ public void startRequesting(String packageName, String providerName, long intervalMs) {
+ PackageProviderKey key = new PackageProviderKey(packageName, providerName);
+ PackageStatistics stats = statistics.get(key);
+ if (stats == null) {
+ stats = new PackageStatistics();
+ statistics.put(key, stats);
+ }
+ stats.startRequesting(intervalMs);
+ }
+
+ /**
+ * Signals that a package has stopped requesting locations.
+ *
+ * @param packageName Name of package that has stopped requesting locations.
+ * @param providerName Provider that is no longer being requested.
+ */
+ public void stopRequesting(String packageName, String providerName) {
+ PackageProviderKey key = new PackageProviderKey(packageName, providerName);
+ PackageStatistics stats = statistics.get(key);
+ if (stats != null) {
+ stats.stopRequesting();
+ } else {
+ // This shouldn't be a possible code path.
+ Log.e(TAG, "Couldn't find package statistics when removing location request.");
+ }
+ }
+
+ /**
+ * A key that holds both package and provider names.
+ */
+ public static class PackageProviderKey {
+ /**
+ * Name of package requesting location.
+ */
+ public final String packageName;
+ /**
+ * Name of provider being requested (e.g. "gps").
+ */
+ public final String providerName;
+
+ public PackageProviderKey(String packageName, String providerName) {
+ this.packageName = packageName;
+ this.providerName = providerName;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof PackageProviderKey)) {
+ return false;
+ }
+
+ PackageProviderKey otherKey = (PackageProviderKey) other;
+ return packageName.equals(otherKey.packageName)
+ && providerName.equals(otherKey.providerName);
+ }
+
+ @Override
+ public int hashCode() {
+ return packageName.hashCode() + 31 * providerName.hashCode();
+ }
+ }
+
+ /**
+ * Usage statistics for a package/provider pair.
+ */
+ public static class PackageStatistics {
+ // Time when this package first requested location.
+ private final long mInitialElapsedTimeMs;
+ // Number of active location requests this package currently has.
+ private int mNumActiveRequests;
+ // Time when this package most recently went from not requesting location to requesting.
+ private long mLastActivitationElapsedTimeMs;
+ // The fastest interval this package has ever requested.
+ private long mFastestIntervalMs;
+ // The slowest interval this package has ever requested.
+ private long mSlowestIntervalMs;
+ // The total time this app has requested location (not including currently running requests).
+ private long mTotalDurationMs;
+
+ private PackageStatistics() {
+ mInitialElapsedTimeMs = SystemClock.elapsedRealtime();
+ mNumActiveRequests = 0;
+ mTotalDurationMs = 0;
+ mFastestIntervalMs = Long.MAX_VALUE;
+ mSlowestIntervalMs = 0;
+ }
+
+ private void startRequesting(long intervalMs) {
+ if (mNumActiveRequests == 0) {
+ mLastActivitationElapsedTimeMs = SystemClock.elapsedRealtime();
+ }
+
+ if (intervalMs < mFastestIntervalMs) {
+ mFastestIntervalMs = intervalMs;
+ }
+
+ if (intervalMs > mSlowestIntervalMs) {
+ mSlowestIntervalMs = intervalMs;
+ }
+
+ mNumActiveRequests++;
+ }
+
+ private void stopRequesting() {
+ if (mNumActiveRequests <= 0) {
+ // Shouldn't be a possible code path
+ Log.e(TAG, "Reference counting corrupted in usage statistics.");
+ return;
+ }
+
+ mNumActiveRequests--;
+ if (mNumActiveRequests == 0) {
+ long lastDurationMs
+ = SystemClock.elapsedRealtime() - mLastActivitationElapsedTimeMs;
+ mTotalDurationMs += lastDurationMs;
+ }
+ }
+
+ /**
+ * Returns the duration that this request has been active.
+ */
+ public long getDurationMs() {
+ long currentDurationMs = mTotalDurationMs;
+ if (mNumActiveRequests > 0) {
+ currentDurationMs
+ += SystemClock.elapsedRealtime() - mLastActivitationElapsedTimeMs;
+ }
+ return currentDurationMs;
+ }
+
+ /**
+ * Returns the time since the initial request in ms.
+ */
+ public long getTimeSinceFirstRequestMs() {
+ return SystemClock.elapsedRealtime() - mInitialElapsedTimeMs;
+ }
+
+ /**
+ * Returns the fastest interval that has been tracked.
+ */
+ public long getFastestIntervalMs() {
+ return mFastestIntervalMs;
+ }
+
+ /**
+ * Returns the slowest interval that has been tracked.
+ */
+ public long getSlowestIntervalMs() {
+ return mSlowestIntervalMs;
+ }
+
+ /**
+ * Returns true if a request is active for these tracked statistics.
+ */
+ public boolean isActive() {
+ return mNumActiveRequests > 0;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder();
+ if (mFastestIntervalMs == mSlowestIntervalMs) {
+ s.append("Interval ").append(mFastestIntervalMs / 1000).append(" seconds");
+ } else {
+ s.append("Min interval ").append(mFastestIntervalMs / 1000).append(" seconds");
+ s.append(": Max interval ").append(mSlowestIntervalMs / 1000).append(" seconds");
+ }
+ s.append(": Duration requested ")
+ .append((getDurationMs() / 1000) / 60)
+ .append(" out of the last ")
+ .append((getTimeSinceFirstRequestMs() / 1000) / 60)
+ .append(" minutes");
+ if (isActive()) {
+ s.append(": Currently active");
+ }
+ return s.toString();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java
index 36c43ff..8578761 100644
--- a/services/core/java/com/android/server/location/MockProvider.java
+++ b/services/core/java/com/android/server/location/MockProvider.java
@@ -16,7 +16,6 @@
package com.android.server.location;
-import android.location.Criteria;
import android.location.ILocationManager;
import android.location.Location;
import android.location.LocationProvider;
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
new file mode 100644
index 0000000..89acec9
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import android.content.Intent;
+import android.media.session.IMediaController;
+import android.media.session.IMediaControllerCallback;
+import android.media.session.IMediaSession;
+import android.media.session.IMediaSessionCallback;
+import android.media.RemoteControlClient;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.KeyEvent;
+
+import java.util.ArrayList;
+
+/**
+ * This is the system implementation of a Session. Apps will interact with the
+ * MediaSession wrapper class instead.
+ */
+public class MediaSessionRecord implements IBinder.DeathRecipient {
+ private static final String TAG = "MediaSessionImpl";
+
+ private final int mPid;
+ private final String mPackageName;
+ private final String mTag;
+ private final ControllerStub mController;
+ private final SessionStub mSession;
+ private final SessionCb mSessionCb;
+ private final MediaSessionService mService;
+
+ private final ArrayList<IMediaControllerCallback> mSessionCallbacks =
+ new ArrayList<IMediaControllerCallback>();
+
+ private int mPlaybackState = RemoteControlClient.PLAYSTATE_NONE;
+
+ public MediaSessionRecord(int pid, String packageName, IMediaSessionCallback cb, String tag,
+ MediaSessionService service) {
+ mPid = pid;
+ mPackageName = packageName;
+ mTag = tag;
+ mController = new ControllerStub();
+ mSession = new SessionStub();
+ mSessionCb = new SessionCb(cb);
+ mService = service;
+ }
+
+ public IMediaSession getSessionBinder() {
+ return mSession;
+ }
+
+ public IMediaController getControllerBinder() {
+ return mController;
+ }
+
+ public void setPlaybackStateInternal(int state) {
+ mPlaybackState = state;
+ for (int i = mSessionCallbacks.size() - 1; i >= 0; i--) {
+ IMediaControllerCallback cb = mSessionCallbacks.get(i);
+ try {
+ cb.onPlaybackUpdate(state);
+ } catch (RemoteException e) {
+ Log.d(TAG, "SessionCallback object dead in setPlaybackState.", e);
+ mSessionCallbacks.remove(i);
+ }
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ mService.sessionDied(this);
+ }
+
+ private void onDestroy() {
+ mService.destroySession(this);
+ }
+
+ private final class SessionStub extends IMediaSession.Stub {
+
+ @Override
+ public void setPlaybackState(int state) throws RemoteException {
+ setPlaybackStateInternal(state);
+ }
+
+ @Override
+ public void destroy() throws RemoteException {
+ onDestroy();
+ }
+
+ @Override
+ public void sendEvent(Bundle data) throws RemoteException {
+ }
+
+ @Override
+ public IMediaController getMediaSessionToken() throws RemoteException {
+ return mController;
+ }
+
+ @Override
+ public void setMetadata(Bundle metadata) throws RemoteException {
+ }
+
+ @Override
+ public void setRouteState(Bundle routeState) throws RemoteException {
+ }
+
+ @Override
+ public void setRoute(Bundle medaiRouteDescriptor) throws RemoteException {
+ }
+
+ }
+
+ class SessionCb {
+ private final IMediaSessionCallback mCb;
+
+ public SessionCb(IMediaSessionCallback cb) {
+ mCb = cb;
+ }
+
+ public void sendMediaButton(KeyEvent keyEvent) {
+ Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+ mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+ try {
+ mCb.onMediaButton(mediaButtonIntent);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Controller object dead in sendMediaRequest.", e);
+ onDestroy();
+ }
+ }
+
+ public void sendCommand(String command, Bundle extras) {
+ try {
+ mCb.onCommand(command, extras);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Controller object dead in sendCommand.", e);
+ onDestroy();
+ }
+ }
+
+ public void registerCallbackListener(IMediaSessionCallback cb) {
+
+ }
+
+ }
+
+ class ControllerStub extends IMediaController.Stub {
+ /*
+ */
+ @Override
+ public void sendCommand(String command, Bundle extras) throws RemoteException {
+ mSessionCb.sendCommand(command, extras);
+ }
+
+ @Override
+ public void sendMediaButton(KeyEvent mediaButtonIntent) {
+ mSessionCb.sendMediaButton(mediaButtonIntent);
+ }
+
+ /*
+ */
+ @Override
+ public void registerCallbackListener(IMediaControllerCallback cb) throws RemoteException {
+ if (!mSessionCallbacks.contains(cb)) {
+ mSessionCallbacks.add(cb);
+ }
+ }
+
+ /*
+ */
+ @Override
+ public void unregisterCallbackListener(IMediaControllerCallback cb)
+ throws RemoteException {
+ mSessionCallbacks.remove(cb);
+ }
+
+ /*
+ */
+ @Override
+ public int getPlaybackState() throws RemoteException {
+ return mPlaybackState;
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
new file mode 100644
index 0000000..a7ff926
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import android.content.Context;
+import android.media.session.IMediaSession;
+import android.media.session.IMediaSessionCallback;
+import android.media.session.IMediaSessionManager;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.server.SystemService;
+
+import java.util.ArrayList;
+
+/**
+ * System implementation of MediaSessionManager
+ */
+public class MediaSessionService extends SystemService {
+ private static final String TAG = "MediaSessionService";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final SessionManagerImpl mSessionManagerImpl;
+
+ private final ArrayList<MediaSessionRecord> mSessions
+ = new ArrayList<MediaSessionRecord>();
+ private final Object mLock = new Object();
+
+ public MediaSessionService(Context context) {
+ super(context);
+ mSessionManagerImpl = new SessionManagerImpl();
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
+ }
+
+ void sessionDied(MediaSessionRecord session) {
+ synchronized (mSessions) {
+ destroySessionLocked(session);
+ }
+ }
+
+ void destroySession(MediaSessionRecord session) {
+ synchronized (mSessions) {
+ destroySessionLocked(session);
+ }
+ }
+
+ private void destroySessionLocked(MediaSessionRecord session) {
+ mSessions.remove(session);
+ }
+
+ private void enforcePackageName(String packageName, int uid) {
+ if (TextUtils.isEmpty(packageName)) {
+ throw new IllegalArgumentException("packageName may not be empty");
+ }
+ String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
+ final int packageCount = packages.length;
+ for (int i = 0; i < packageCount; i++) {
+ if (packageName.equals(packages[i])) {
+ return;
+ }
+ }
+ throw new IllegalArgumentException("packageName is not owned by the calling process");
+ }
+
+ private MediaSessionRecord createSessionInternal(int pid, String packageName,
+ IMediaSessionCallback cb, String tag) {
+ synchronized (mLock) {
+ return createSessionLocked(pid, packageName, cb, tag);
+ }
+ }
+
+ private MediaSessionRecord createSessionLocked(int pid, String packageName,
+ IMediaSessionCallback cb, String tag) {
+ final MediaSessionRecord session = new MediaSessionRecord(pid, packageName, cb, tag, this);
+ try {
+ cb.asBinder().linkToDeath(session, 0);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Media Session owner died prematurely.", e);
+ }
+ synchronized (mSessions) {
+ mSessions.add(session);
+ }
+ if (DEBUG) {
+ Log.d(TAG, "Created session for package " + packageName + " with tag " + tag);
+ }
+ return session;
+ }
+
+ class SessionManagerImpl extends IMediaSessionManager.Stub {
+ @Override
+ public IMediaSession createSession(String packageName, IMediaSessionCallback cb, String tag)
+ throws RemoteException {
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ enforcePackageName(packageName, uid);
+ if (cb == null) {
+ throw new IllegalArgumentException("Controller callback cannot be null");
+ }
+ return createSessionInternal(pid, packageName, cb, tag).getSessionBinder();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 5d6adc2..271e9e9 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -464,21 +464,21 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private NetworkStatsCollection mUidTagComplete;
private NetworkStatsCollection getUidComplete() {
- if (mUidComplete == null) {
- synchronized (mStatsLock) {
+ synchronized (mStatsLock) {
+ if (mUidComplete == null) {
mUidComplete = mUidRecorder.getOrLoadCompleteLocked();
}
+ return mUidComplete;
}
- return mUidComplete;
}
private NetworkStatsCollection getUidTagComplete() {
- if (mUidTagComplete == null) {
- synchronized (mStatsLock) {
+ synchronized (mStatsLock) {
+ if (mUidTagComplete == null) {
mUidTagComplete = mUidTagRecorder.getOrLoadCompleteLocked();
}
+ return mUidTagComplete;
}
- return mUidTagComplete;
}
@Override
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index df2aaca..243bd74 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -16,6 +16,8 @@
package com.android.server.notification;
+import android.os.IBinder;
+
public interface NotificationDelegate {
void onSetDisabled(int status);
void onClearAll();
@@ -24,4 +26,5 @@ public interface NotificationDelegate {
void onNotificationError(String pkg, String tag, int id,
int uid, int initialPid, String message);
void onPanelRevealed();
+ boolean allowDisable(int what, IBinder token, String pkg);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index bd4442e..e37bb9c 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -154,7 +154,7 @@ public class NotificationManagerService extends SystemService {
private long[] mFallbackVibrationPattern;
boolean mSystemReady;
- int mDisabledNotifications;
+ private boolean mDisableNotificationAlerts;
NotificationRecord mSoundNotification;
NotificationRecord mVibrateNotification;
@@ -202,6 +202,19 @@ public class NotificationManagerService extends SystemService {
final ArrayList<NotificationScorer> mScorers = new ArrayList<NotificationScorer>();
+ private int mZenMode;
+ private int mPreZenAlarmVolume = -1;
+ private int mPreZenRingerMode = -1;
+ // temporary, until we update apps to provide metadata
+ private static final Set<String> CALL_PACKAGES = new HashSet<String>(Arrays.asList(
+ "com.google.android.dialer",
+ "com.android.phone"
+ ));
+ private static final Set<String> ALARM_PACKAGES = new HashSet<String>(Arrays.asList(
+ "com.google.android.deskclock"
+ ));
+ private static final String EXTRA_INTERCEPT = "android.intercept";
+
private class NotificationListenerInfo implements IBinder.DeathRecipient {
INotificationListener listener;
ComponentName component;
@@ -870,8 +883,8 @@ public class NotificationManagerService extends SystemService {
@Override
public void onSetDisabled(int status) {
synchronized (mNotificationList) {
- mDisabledNotifications = status;
- if ((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
+ mDisableNotificationAlerts = (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
+ if (mDisableNotificationAlerts) {
// cancel whatever's going on
long identity = Binder.clearCallingIdentity();
try {
@@ -969,6 +982,14 @@ public class NotificationManagerService extends SystemService {
}
Binder.restoreCallingIdentity(ident);
}
+
+ @Override
+ public boolean allowDisable(int what, IBinder token, String pkg) {
+ if (mZenMode == Settings.Global.ZEN_MODE_FULL && isCall(pkg, null)) {
+ return false;
+ }
+ return true;
+ }
};
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@@ -1053,8 +1074,8 @@ public class NotificationManagerService extends SystemService {
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
mScreenOn = false;
} else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
- mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(
- TelephonyManager.EXTRA_STATE_OFFHOOK));
+ mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
+ .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));
updateNotificationPulse();
} else if (action.equals(Intent.ACTION_USER_STOPPED)) {
int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
@@ -1078,6 +1099,12 @@ public class NotificationManagerService extends SystemService {
private final Uri ENABLED_NOTIFICATION_LISTENERS_URI
= Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
+ private final Uri ZEN_MODE
+ = Settings.Global.getUriFor(Settings.Global.ZEN_MODE);
+
+ private final Uri MODE_RINGER
+ = Settings.Global.getUriFor(Settings.Global.MODE_RINGER);
+
SettingsObserver(Handler handler) {
super(handler);
}
@@ -1088,6 +1115,10 @@ public class NotificationManagerService extends SystemService {
false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI,
false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(ZEN_MODE,
+ false, this);
+ resolver.registerContentObserver(MODE_RINGER,
+ false, this);
update(null);
}
@@ -1108,6 +1139,12 @@ public class NotificationManagerService extends SystemService {
if (uri == null || ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri)) {
rebindListenerServices();
}
+ if (ZEN_MODE.equals(uri)) {
+ updateZenMode();
+ }
+ if (MODE_RINGER.equals(uri)) {
+ updateRingerMode();
+ }
}
}
@@ -1171,8 +1208,10 @@ public class NotificationManagerService extends SystemService {
// flag at least once and we'll go back to 0 after that.
if (0 == Settings.Global.getInt(getContext().getContentResolver(),
Settings.Global.DEVICE_PROVISIONED, 0)) {
- mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
+ mDisableNotificationAlerts = true;
}
+ updateRingerMode();
+ updateZenMode();
// register for various Intents
IntentFilter filter = new IntentFilter();
@@ -1623,8 +1662,10 @@ public class NotificationManagerService extends SystemService {
pw.println(" mSoundNotification=" + mSoundNotification);
pw.println(" mVibrateNotification=" + mVibrateNotification);
- pw.println(" mDisabledNotifications=0x"
- + Integer.toHexString(mDisabledNotifications));
+ pw.println(" mDisableNotificationAlerts=" + mDisableNotificationAlerts);
+ pw.println(" mZenMode=" + Settings.Global.zenModeToString(mZenMode));
+ pw.println(" mPreZenAlarmVolume=" + mPreZenAlarmVolume);
+ pw.println(" mPreZenRingerMode=" + mPreZenRingerMode);
pw.println(" mSystemReady=" + mSystemReady);
pw.println(" mArchive=" + mArchive.toString());
Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
@@ -1768,9 +1809,13 @@ public class NotificationManagerService extends SystemService {
return;
}
- // Should this notification make noise, vibe, or use the LED?
- final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD);
+ // Is this notification intercepted by zen mode?
+ final boolean intercept = shouldIntercept(pkg, notification);
+ notification.extras.putBoolean(EXTRA_INTERCEPT, intercept);
+ // Should this notification make noise, vibe, or use the LED?
+ final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) && !intercept;
+ if (DBG) Slog.v(TAG, "canInterrupt=" + canInterrupt + " intercept=" + intercept);
synchronized (mNotificationList) {
final StatusBarNotification n = new StatusBarNotification(
pkg, id, tag, callingUid, callingPid, score, notification, user);
@@ -1852,8 +1897,7 @@ public class NotificationManagerService extends SystemService {
}
// If we're not supposed to beep, vibrate, etc. then don't.
- if (((mDisabledNotifications
- & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
+ if (!mDisableNotificationAlerts
&& (!(old != null
&& (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
&& (r.getUserId() == UserHandle.USER_ALL ||
@@ -1861,7 +1905,7 @@ public class NotificationManagerService extends SystemService {
&& canInterrupt
&& mSystemReady
&& mAudioManager != null) {
-
+ if (DBG) Slog.v(TAG, "Interrupting!");
// sound
// should we use the default notification sound? (indicated either by
@@ -1906,6 +1950,8 @@ public class NotificationManagerService extends SystemService {
final IRingtonePlayer player =
mAudioManager.getRingtonePlayer();
if (player != null) {
+ if (DBG) Slog.v(TAG, "Playing sound " + soundUri
+ + " on stream " + audioStreamType);
player.playAsync(soundUri, user, looping, audioStreamType);
}
} catch (RemoteException e) {
@@ -2453,4 +2499,67 @@ public class NotificationManagerService extends SystemService {
updateLightsLocked();
}
}
+
+ private void updateRingerMode() {
+ final int ringerMode = Settings.Global.getInt(getContext().getContentResolver(),
+ Settings.Global.MODE_RINGER, -1);
+ final boolean nonSilentRingerMode = ringerMode == AudioManager.RINGER_MODE_NORMAL
+ || ringerMode == AudioManager.RINGER_MODE_VIBRATE;
+ if (mZenMode == Settings.Global.ZEN_MODE_FULL && nonSilentRingerMode) {
+ Settings.Global.putInt(getContext().getContentResolver(),
+ Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
+ }
+ }
+
+ private void updateZenMode() {
+ mZenMode = Settings.Global.getInt(getContext().getContentResolver(),
+ Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
+ if (DBG) Slog.d(TAG, "updateZenMode " + Settings.Global.zenModeToString(mZenMode));
+ if (mAudioManager != null) {
+ if (mZenMode == Settings.Global.ZEN_MODE_FULL) {
+ // calls vibrate if ringer mode = vibrate, so set the ringer mode as well
+ mPreZenRingerMode = mAudioManager.getRingerMode();
+ if (DBG) Slog.d(TAG, "Muting calls mPreZenRingerMode=" + mPreZenRingerMode);
+ mAudioManager.setStreamMute(AudioManager.STREAM_RING, true);
+ mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
+ // alarms don't simply respect mute, so set the volume as well
+ mPreZenAlarmVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM);
+ if (DBG) Slog.d(TAG, "Muting alarms mPreZenAlarmVolume=" + mPreZenAlarmVolume);
+ mAudioManager.setStreamMute(AudioManager.STREAM_ALARM, true);
+ mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 0, 0);
+ } else {
+ if (DBG) Slog.d(TAG, "Unmuting calls");
+ mAudioManager.setStreamMute(AudioManager.STREAM_RING, false);
+ if (mPreZenRingerMode != -1) {
+ if (DBG) Slog.d(TAG, "Restoring ringer mode to " + mPreZenRingerMode);
+ mAudioManager.setRingerMode(mPreZenRingerMode);
+ mPreZenRingerMode = -1;
+ }
+ if (DBG) Slog.d(TAG, "Unmuting alarms");
+ mAudioManager.setStreamMute(AudioManager.STREAM_ALARM, false);
+ if (mPreZenAlarmVolume != -1) {
+ if (DBG) Slog.d(TAG, "Restoring STREAM_ALARM to " + mPreZenAlarmVolume);
+ mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, mPreZenAlarmVolume, 0);
+ mPreZenAlarmVolume = -1;
+ }
+ }
+ }
+ }
+
+ private boolean isCall(String pkg, Notification n) {
+ return CALL_PACKAGES.contains(pkg);
+ }
+
+ private boolean isAlarm(String pkg, Notification n) {
+ return ALARM_PACKAGES.contains(pkg);
+ }
+
+ private boolean shouldIntercept(String pkg, Notification n) {
+ if (mZenMode == Settings.Global.ZEN_MODE_LIMITED) {
+ return !isCall(pkg, n) && !isAlarm(pkg, n);
+ } else if (mZenMode == Settings.Global.ZEN_MODE_FULL) {
+ return true;
+ }
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/pm/KeySetManager.java b/services/core/java/com/android/server/pm/KeySetManager.java
index 66dc1d1..1056cd0 100644
--- a/services/core/java/com/android/server/pm/KeySetManager.java
+++ b/services/core/java/com/android/server/pm/KeySetManager.java
@@ -20,13 +20,11 @@ import android.content.pm.KeySet;
import android.content.pm.PackageParser;
import android.os.Binder;
import android.util.Base64;
-import android.util.Log;
import android.util.LongSparseArray;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.PublicKey;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
diff --git a/services/core/java/com/android/server/pm/PackageKeySetData.java b/services/core/java/com/android/server/pm/PackageKeySetData.java
index cb60621..ebded28 100644
--- a/services/core/java/com/android/server/pm/PackageKeySetData.java
+++ b/services/core/java/com/android/server/pm/PackageKeySetData.java
@@ -18,9 +18,7 @@ package com.android.server.pm;
import java.util.Arrays;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Map;
-import java.util.Set;
public class PackageKeySetData {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 70eee7e..7f00ce7 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -220,6 +220,7 @@ public class PackageManagerService extends IPackageManager.Stub {
static final int SCAN_DEFER_DEX = 1<<7;
static final int SCAN_BOOTING = 1<<8;
static final int SCAN_TRUSTED_OVERLAY = 1<<9;
+ static final int SCAN_DELETE_DATA_ON_FAILURES = 1<<10;
static final int REMOVE_CHATTY = 1<<16;
@@ -343,7 +344,6 @@ public class PackageManagerService extends IPackageManager.Stub {
new HashMap<String, PackageParser.Package>();
// Information for the parser to write more useful error messages.
- File mScanningPath;
int mLastScanError;
// ----------------------------------------------------------------
@@ -882,7 +882,7 @@ public class PackageManagerService extends IPackageManager.Stub {
int[] uidArray = new int[] { res.pkg.applicationInfo.uid };
ArrayList<String> pkgList = new ArrayList<String>(1);
pkgList.add(res.pkg.applicationInfo.packageName);
- sendResourcesChangedBroadcast(true, false,
+ sendResourcesChangedBroadcast(true, true,
pkgList,uidArray, null);
}
}
@@ -2777,6 +2777,63 @@ public class PackageManagerService extends IPackageManager.Stub {
return null;
}
+ private ResolveInfo findPersistentPreferredActivityLP(Intent intent, String resolvedType,
+ int flags, List<ResolveInfo> query, boolean debug, int userId) {
+ final int N = query.size();
+ PersistentPreferredIntentResolver ppir = mSettings.mPersistentPreferredActivities
+ .get(userId);
+ // Get the list of persistent preferred activities that handle the intent
+ if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for presistent preferred activities...");
+ List<PersistentPreferredActivity> pprefs = ppir != null
+ ? ppir.queryIntent(intent, resolvedType,
+ (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId)
+ : null;
+ if (pprefs != null && pprefs.size() > 0) {
+ final int M = pprefs.size();
+ for (int i=0; i<M; i++) {
+ final PersistentPreferredActivity ppa = pprefs.get(i);
+ if (DEBUG_PREFERRED || debug) {
+ Slog.v(TAG, "Checking PersistentPreferredActivity ds="
+ + (ppa.countDataSchemes() > 0 ? ppa.getDataScheme(0) : "<none>")
+ + "\n component=" + ppa.mComponent);
+ ppa.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
+ }
+ final ActivityInfo ai = getActivityInfo(ppa.mComponent,
+ flags | PackageManager.GET_DISABLED_COMPONENTS, userId);
+ if (DEBUG_PREFERRED || debug) {
+ Slog.v(TAG, "Found persistent preferred activity:");
+ if (ai != null) {
+ ai.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
+ } else {
+ Slog.v(TAG, " null");
+ }
+ }
+ if (ai == null) {
+ // This previously registered persistent preferred activity
+ // component is no longer known. Ignore it and do NOT remove it.
+ continue;
+ }
+ for (int j=0; j<N; j++) {
+ final ResolveInfo ri = query.get(j);
+ if (!ri.activityInfo.applicationInfo.packageName
+ .equals(ai.applicationInfo.packageName)) {
+ continue;
+ }
+ if (!ri.activityInfo.name.equals(ai.name)) {
+ continue;
+ }
+ // Found a persistent preference that can handle the intent.
+ if (DEBUG_PREFERRED || debug) {
+ Slog.v(TAG, "Returning persistent preferred activity: " +
+ ri.activityInfo.packageName + "/" + ri.activityInfo.name);
+ }
+ return ri;
+ }
+ }
+ }
+ return null;
+ }
+
ResolveInfo findPreferredActivity(Intent intent, String resolvedType, int flags,
List<ResolveInfo> query, int priority, boolean always,
boolean removeMatches, boolean debug, int userId) {
@@ -2787,6 +2844,16 @@ public class PackageManagerService extends IPackageManager.Stub {
intent = intent.getSelector();
}
if (DEBUG_PREFERRED) intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
+
+ // Try to find a matching persistent preferred activity.
+ ResolveInfo pri = findPersistentPreferredActivityLP(intent, resolvedType, flags, query,
+ debug, userId);
+
+ // If a persistent preferred activity matched, use it.
+ if (pri != null) {
+ return pri;
+ }
+
PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId);
// Get the list of preferred activities that handle the intent
if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for preferred activities...");
@@ -4258,7 +4325,6 @@ public class PackageManagerService extends IPackageManager.Stub {
mLastScanError = PackageManager.INSTALL_FAILED_INVALID_APK;
return null;
}
- mScanningPath = scanFile;
if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
@@ -4278,7 +4344,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (mAndroidApplication != null) {
Slog.w(TAG, "*************************************************");
Slog.w(TAG, "Core android package being redefined. Skipping.");
- Slog.w(TAG, " file=" + mScanningPath);
+ Slog.w(TAG, " file=" + scanFile);
Slog.w(TAG, "*************************************************");
mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
return null;
@@ -4758,6 +4824,10 @@ public class PackageManagerService extends IPackageManager.Stub {
if ((scanMode&SCAN_NO_DEX) == 0) {
if (performDexOptLI(pkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0, false)
== DEX_OPT_FAILED) {
+ if ((scanMode & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
+ removeDataDirsLI(pkg.packageName);
+ }
+
mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT;
return null;
}
@@ -4835,6 +4905,10 @@ public class PackageManagerService extends IPackageManager.Stub {
PackageParser.Package clientPkg = clientLibPkgs.get(i);
if (performDexOptLI(clientPkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0, false)
== DEX_OPT_FAILED) {
+ if ((scanMode & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
+ removeDataDirsLI(pkg.packageName);
+ }
+
mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT;
return null;
}
@@ -5105,8 +5179,9 @@ public class PackageManagerService extends IPackageManager.Stub {
// discard the previous declaration and consider the system's to be
// canonical.
if (isSystemApp(p.owner)) {
- Slog.i(TAG, "New decl " + p.owner + " of permission "
- + p.info.name + " is system");
+ String msg = "New decl " + p.owner + " of permission "
+ + p.info.name + " is system";
+ reportSettingsProblem(Log.WARN, msg);
bp.sourcePackage = null;
}
}
@@ -6229,6 +6304,11 @@ public class PackageManagerService extends IPackageManager.Stub {
}
public final void addProvider(PackageParser.Provider p) {
+ if (mProviders.containsKey(p.getComponentName())) {
+ Slog.w(TAG, "Provider " + p.getComponentName() + " already defined; ignoring");
+ return;
+ }
+
mProviders.put(p.getComponentName(), p);
if (DEBUG_SHOW_INFO) {
Log.v(TAG, " "
@@ -9215,7 +9295,7 @@ public class PackageManagerService extends IPackageManager.Stub {
replacePackageLI(pkg, parseFlags, scanMode, args.user,
installerPackageName, res);
} else {
- installNewPackageLI(pkg, parseFlags, scanMode, args.user,
+ installNewPackageLI(pkg, parseFlags, scanMode | SCAN_DELETE_DATA_ON_FAILURES, args.user,
installerPackageName, res);
}
synchronized (mPackages) {
@@ -10330,6 +10410,71 @@ public class PackageManagerService extends IPackageManager.Stub {
}
@Override
+ public void addPersistentPreferredActivity(IntentFilter filter, ComponentName activity,
+ int userId) {
+ int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.SYSTEM_UID) {
+ throw new SecurityException(
+ "addPersistentPreferredActivity can only be run by the system");
+ }
+ if (filter.countActions() == 0) {
+ Slog.w(TAG, "Cannot set a preferred activity with no filter actions");
+ return;
+ }
+ synchronized (mPackages) {
+ Slog.i(TAG, "Adding persistent preferred activity " + activity + " for user " + userId +
+ " :");
+ filter.dump(new LogPrinter(Log.INFO, TAG), " ");
+ mSettings.editPersistentPreferredActivitiesLPw(userId).addFilter(
+ new PersistentPreferredActivity(filter, activity));
+ mSettings.writePackageRestrictionsLPr(userId);
+ }
+ }
+
+ @Override
+ public void clearPackagePersistentPreferredActivities(String packageName, int userId) {
+ int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.SYSTEM_UID) {
+ throw new SecurityException(
+ "clearPackagePersistentPreferredActivities can only be run by the system");
+ }
+ ArrayList<PersistentPreferredActivity> removed = null;
+ boolean changed = false;
+ synchronized (mPackages) {
+ for (int i=0; i<mSettings.mPersistentPreferredActivities.size(); i++) {
+ final int thisUserId = mSettings.mPersistentPreferredActivities.keyAt(i);
+ PersistentPreferredIntentResolver ppir = mSettings.mPersistentPreferredActivities
+ .valueAt(i);
+ if (userId != thisUserId) {
+ continue;
+ }
+ Iterator<PersistentPreferredActivity> it = ppir.filterIterator();
+ while (it.hasNext()) {
+ PersistentPreferredActivity ppa = it.next();
+ // Mark entry for removal only if it matches the package name.
+ if (ppa.mComponent.getPackageName().equals(packageName)) {
+ if (removed == null) {
+ removed = new ArrayList<PersistentPreferredActivity>();
+ }
+ removed.add(ppa);
+ }
+ }
+ if (removed != null) {
+ for (int j=0; j<removed.size(); j++) {
+ PersistentPreferredActivity ppa = removed.get(j);
+ ppir.removeFilter(ppa);
+ }
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ mSettings.writePackageRestrictionsLPr(userId);
+ }
+ }
+ }
+
+ @Override
public ComponentName getHomeActivities(List<ResolveInfo> allHomeCandidates) {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
@@ -10722,7 +10867,8 @@ public class PackageManagerService extends IPackageManager.Stub {
DumpState dumpState = new DumpState();
boolean fullPreferred = false;
-
+ boolean checkin = false;
+
String packageName = null;
int opti = 0;
@@ -10736,7 +10882,8 @@ public class PackageManagerService extends IPackageManager.Stub {
// Right now we only know how to print all.
} else if ("-h".equals(opt)) {
pw.println("Package manager dump options:");
- pw.println(" [-h] [-f] [cmd] ...");
+ pw.println(" [-h] [-f] [--checkin] [cmd] ...");
+ pw.println(" --checkin: dump for a checkin");
pw.println(" -f: print details of intent filters");
pw.println(" -h: print this help");
pw.println(" cmd may be one of:");
@@ -10754,13 +10901,15 @@ public class PackageManagerService extends IPackageManager.Stub {
pw.println(" <package.name>: info about given package");
pw.println(" k[eysets]: print known keysets");
return;
+ } else if ("--checkin".equals(opt)) {
+ checkin = true;
} else if ("-f".equals(opt)) {
dumpState.setOptionEnabled(DumpState.OPTION_SHOW_FILTERS);
} else {
pw.println("Unknown argument: " + opt + "; use -h for help");
}
}
-
+
// Is the caller requesting to dump a particular piece of data?
if (opti < args.length) {
String cmd = args[opti];
@@ -10802,17 +10951,26 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+ if (checkin) {
+ pw.println("vers,1");
+ }
+
// reader
synchronized (mPackages) {
if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) {
- if (dumpState.onTitlePrinted())
- pw.println();
- pw.println("Verifiers:");
- pw.print(" Required: ");
- pw.print(mRequiredVerifierPackage);
- pw.print(" (uid=");
- pw.print(getPackageUid(mRequiredVerifierPackage, 0));
- pw.println(")");
+ if (!checkin) {
+ if (dumpState.onTitlePrinted())
+ pw.println();
+ pw.println("Verifiers:");
+ pw.print(" Required: ");
+ pw.print(mRequiredVerifierPackage);
+ pw.print(" (uid=");
+ pw.print(getPackageUid(mRequiredVerifierPackage, 0));
+ pw.println(")");
+ } else if (mRequiredVerifierPackage != null) {
+ pw.print("vrfy,"); pw.print(mRequiredVerifierPackage);
+ pw.print(","); pw.println(getPackageUid(mRequiredVerifierPackage, 0));
+ }
}
if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) {
@@ -10821,21 +10979,37 @@ public class PackageManagerService extends IPackageManager.Stub {
while (it.hasNext()) {
String name = it.next();
SharedLibraryEntry ent = mSharedLibraries.get(name);
- if (!printedHeader) {
- if (dumpState.onTitlePrinted())
- pw.println();
- pw.println("Libraries:");
- printedHeader = true;
+ if (!checkin) {
+ if (!printedHeader) {
+ if (dumpState.onTitlePrinted())
+ pw.println();
+ pw.println("Libraries:");
+ printedHeader = true;
+ }
+ pw.print(" ");
+ } else {
+ pw.print("lib,");
}
- pw.print(" ");
pw.print(name);
- pw.print(" -> ");
+ if (!checkin) {
+ pw.print(" -> ");
+ }
if (ent.path != null) {
- pw.print("(jar) ");
- pw.print(ent.path);
+ if (!checkin) {
+ pw.print("(jar) ");
+ pw.print(ent.path);
+ } else {
+ pw.print(",jar,");
+ pw.print(ent.path);
+ }
} else {
- pw.print("(apk) ");
- pw.print(ent.apk);
+ if (!checkin) {
+ pw.print("(apk) ");
+ pw.print(ent.apk);
+ } else {
+ pw.print(",apk,");
+ pw.print(ent.apk);
+ }
}
pw.println();
}
@@ -10844,16 +11018,22 @@ public class PackageManagerService extends IPackageManager.Stub {
if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) {
if (dumpState.onTitlePrinted())
pw.println();
- pw.println("Features:");
+ if (!checkin) {
+ pw.println("Features:");
+ }
Iterator<String> it = mAvailableFeatures.keySet().iterator();
while (it.hasNext()) {
String name = it.next();
- pw.print(" ");
+ if (!checkin) {
+ pw.print(" ");
+ } else {
+ pw.print("feat,");
+ }
pw.println(name);
}
}
- if (dumpState.isDumping(DumpState.DUMP_RESOLVERS)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_RESOLVERS)) {
if (mActivities.dump(pw, dumpState.getTitlePrinted() ? "\nActivity Resolver Table:"
: "Activity Resolver Table:", " ", packageName,
dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) {
@@ -10876,7 +11056,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
- if (dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
for (int i=0; i<mSettings.mPreferredActivities.size(); i++) {
PreferredIntentResolver pir = mSettings.mPreferredActivities.valueAt(i);
int user = mSettings.mPreferredActivities.keyAt(i);
@@ -10890,7 +11070,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
- if (dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)) {
pw.flush();
FileOutputStream fout = new FileOutputStream(fd);
BufferedOutputStream str = new BufferedOutputStream(fout);
@@ -10912,11 +11092,11 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
- if (dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
mSettings.dumpPermissionsLPr(pw, packageName, dumpState);
}
- if (dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
boolean printedSomething = false;
for (PackageParser.Provider p : mProviders.mProviders.values()) {
if (packageName != null && !packageName.equals(p.info.packageName)) {
@@ -10953,19 +11133,19 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
- if (dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
mSettings.mKeySetManager.dump(pw, packageName, dumpState);
}
if (dumpState.isDumping(DumpState.DUMP_PACKAGES)) {
- mSettings.dumpPackagesLPr(pw, packageName, dumpState);
+ mSettings.dumpPackagesLPr(pw, packageName, dumpState, checkin);
}
- if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) {
mSettings.dumpSharedUsersLPr(pw, packageName, dumpState);
}
- if (dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) {
+ if (!checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) {
if (dumpState.onTitlePrinted())
pw.println();
mSettings.dumpReadMessagesLPr(pw, dumpState);
@@ -11204,7 +11384,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (uidArr != null) {
extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidArr);
}
- if (replacing && !mediaStatus) {
+ if (replacing) {
extras.putBoolean(Intent.EXTRA_REPLACING, replacing);
}
String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 7747c8f..2a5698b 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -20,14 +20,11 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageUserState;
-import android.content.pm.UserInfo;
import android.util.SparseArray;
import java.io.File;
import java.util.HashSet;
-import java.util.List;
/**
* Settings base class for pending and resolved classes.
diff --git a/services/core/java/com/android/server/pm/PersistentPreferredActivity.java b/services/core/java/com/android/server/pm/PersistentPreferredActivity.java
new file mode 100644
index 0000000..4284a6d
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PersistentPreferredActivity.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import android.content.ComponentName;
+import android.content.IntentFilter;
+import android.util.Log;
+
+import java.io.IOException;
+
+class PersistentPreferredActivity extends IntentFilter {
+ private static final String ATTR_NAME = "name"; // component name
+ private static final String ATTR_FILTER = "filter"; // filter
+
+ private static final String TAG = "PersistentPreferredActivity";
+
+ private static final boolean DEBUG_FILTERS = false;
+
+ final ComponentName mComponent;
+
+ PersistentPreferredActivity(IntentFilter filter, ComponentName activity) {
+ super(filter);
+ mComponent = activity;
+ }
+
+ PersistentPreferredActivity(XmlPullParser parser) throws XmlPullParserException, IOException {
+ String shortComponent = parser.getAttributeValue(null, ATTR_NAME);
+ mComponent = ComponentName.unflattenFromString(shortComponent);
+ if (mComponent == null) {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings <hard-preferred-activity>: " +
+ "Bad activity name " + shortComponent +
+ " at " + parser.getPositionDescription());
+ }
+ int outerDepth = parser.getDepth();
+ String tagName = parser.getName();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ tagName = parser.getName();
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ } else if (type == XmlPullParser.START_TAG) {
+ if (tagName.equals(ATTR_FILTER)) {
+ break;
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element under <hard-preferred-activity>: " + tagName +
+ " at " + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ }
+ if (tagName.equals(ATTR_FILTER)) {
+ readFromXml(parser);
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Missing element under <hard-preferred-activity>: filter at " +
+ parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+
+ public void writeToXml(XmlSerializer serializer) throws IOException {
+ serializer.attribute(null, ATTR_NAME, mComponent.flattenToShortString());
+ serializer.startTag(null, ATTR_FILTER);
+ super.writeToXml(serializer);
+ serializer.endTag(null, ATTR_FILTER);
+ }
+
+ @Override
+ public String toString() {
+ return "PersistentPreferredActivity{0x" + Integer.toHexString(System.identityHashCode(this))
+ + " " + mComponent.flattenToShortString() + "}";
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PersistentPreferredIntentResolver.java b/services/core/java/com/android/server/pm/PersistentPreferredIntentResolver.java
new file mode 100644
index 0000000..9c8a9bd
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PersistentPreferredIntentResolver.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import java.io.PrintWriter;
+
+import com.android.server.IntentResolver;
+
+public class PersistentPreferredIntentResolver
+ extends IntentResolver<PersistentPreferredActivity, PersistentPreferredActivity> {
+ @Override
+ protected PersistentPreferredActivity[] newArray(int size) {
+ return new PersistentPreferredActivity[size];
+ }
+
+ @Override
+ protected boolean isPackageForFilter(String packageName, PersistentPreferredActivity filter) {
+ return packageName.equals(filter.mComponent.getPackageName());
+ }
+}
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index 1d68afa..6e12e41 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -26,7 +26,6 @@ import android.util.Xml;
import com.android.internal.util.XmlUtils;
import java.io.File;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index e599409..9236bde 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -45,7 +45,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
-import android.content.pm.KeySet;
import android.content.pm.PackageCleanItem;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
@@ -60,7 +59,6 @@ import android.os.FileUtils;
import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
-import android.util.LongSparseArray;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
@@ -71,7 +69,6 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
-import java.security.PublicKey;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -144,6 +141,11 @@ final class Settings {
final SparseArray<PreferredIntentResolver> mPreferredActivities =
new SparseArray<PreferredIntentResolver>();
+ // The persistent preferred activities of the user's profile/device owner
+ // associated with particular intent filters.
+ final SparseArray<PersistentPreferredIntentResolver> mPersistentPreferredActivities =
+ new SparseArray<PersistentPreferredIntentResolver>();
+
final HashMap<String, SharedUserSetting> mSharedUsers =
new HashMap<String, SharedUserSetting>();
private final ArrayList<Object> mUserIds = new ArrayList<Object>();
@@ -779,6 +781,15 @@ final class Settings {
return pir;
}
+ PersistentPreferredIntentResolver editPersistentPreferredActivitiesLPw(int userId) {
+ PersistentPreferredIntentResolver ppir = mPersistentPreferredActivities.get(userId);
+ if (ppir == null) {
+ ppir = new PersistentPreferredIntentResolver();
+ mPersistentPreferredActivities.put(userId, ppir);
+ }
+ return ppir;
+ }
+
private File getUserPackagesStateFile(int userId) {
return new File(Environment.getUserSystemDirectory(userId), "package-restrictions.xml");
}
@@ -838,6 +849,27 @@ final class Settings {
}
}
+ private void readPersistentPreferredActivitiesLPw(XmlPullParser parser, int userId)
+ throws XmlPullParserException, IOException {
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ String tagName = parser.getName();
+ if (tagName.equals(TAG_ITEM)) {
+ PersistentPreferredActivity ppa = new PersistentPreferredActivity(parser);
+ editPersistentPreferredActivitiesLPw(userId).addFilter(ppa);
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element under <hard-preferred-activities>: " + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ }
+
void readPackageRestrictionsLPr(int userId) {
if (DEBUG_MU) {
Log.i(TAG, "Reading package restrictions for user=" + userId);
@@ -964,6 +996,8 @@ final class Settings {
enabledCaller, enabledComponents, disabledComponents);
} else if (tagName.equals("preferred-activities")) {
readPreferredActivitiesLPw(parser, userId);
+ } else if (tagName.equals("hard-preferred-activities")) {
+ readPersistentPreferredActivitiesLPw(parser, userId);
} else {
Slog.w(PackageManagerService.TAG, "Unknown element under <stopped-packages>: "
+ parser.getName());
@@ -1027,6 +1061,20 @@ final class Settings {
serializer.endTag(null, "preferred-activities");
}
+ void writePersistentPreferredActivitiesLPr(XmlSerializer serializer, int userId)
+ throws IllegalArgumentException, IllegalStateException, IOException {
+ serializer.startTag(null, "hard-preferred-activities");
+ PersistentPreferredIntentResolver ppir = mPersistentPreferredActivities.get(userId);
+ if (ppir != null) {
+ for (final PersistentPreferredActivity ppa : ppir.filterSet()) {
+ serializer.startTag(null, TAG_ITEM);
+ ppa.writeToXml(serializer);
+ serializer.endTag(null, TAG_ITEM);
+ }
+ }
+ serializer.endTag(null, "hard-preferred-activities");
+ }
+
void writePackageRestrictionsLPr(int userId) {
if (DEBUG_MU) {
Log.i(TAG, "Writing package restrictions for user=" + userId);
@@ -1123,6 +1171,8 @@ final class Settings {
writePreferredActivitiesLPr(serializer, userId, true);
+ writePersistentPreferredActivitiesLPr(serializer, userId);
+
serializer.endTag(null, TAG_PACKAGE_RESTRICTIONS);
serializer.endDocument();
@@ -1724,6 +1774,10 @@ final class Settings {
// Upgrading from old single-user implementation;
// these are the preferred activities for user 0.
readPreferredActivitiesLPw(parser, 0);
+ } else if (tagName.equals("hard-preferred-activities")) {
+ // TODO: check whether this is okay! as it is very
+ // similar to how preferred-activities are treated
+ readPersistentPreferredActivitiesLPw(parser, 0);
} else if (tagName.equals("updated-package")) {
readDisabledSysPackageLPw(parser);
} else if (tagName.equals("cleaning-package")) {
@@ -2892,8 +2946,44 @@ final class Settings {
ApplicationInfo.FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE",
};
- void dumpPackageLPr(PrintWriter pw, String prefix, PackageSetting ps, SimpleDateFormat sdf,
- Date date, List<UserInfo> users) {
+ void dumpPackageLPr(PrintWriter pw, String prefix, String checkinTag, PackageSetting ps,
+ SimpleDateFormat sdf, Date date, List<UserInfo> users) {
+ if (checkinTag != null) {
+ pw.print(checkinTag);
+ pw.print(",");
+ pw.print(ps.realName != null ? ps.realName : ps.name);
+ pw.print(",");
+ pw.print(ps.appId);
+ pw.print(",");
+ pw.print(ps.versionCode);
+ pw.print(",");
+ pw.print(ps.firstInstallTime);
+ pw.print(",");
+ pw.print(ps.lastUpdateTime);
+ pw.print(",");
+ pw.print(ps.installerPackageName != null ? ps.installerPackageName : "?");
+ pw.println();
+ for (UserInfo user : users) {
+ pw.print(checkinTag);
+ pw.print("-");
+ pw.print("usr");
+ pw.print(",");
+ pw.print(user.id);
+ pw.print(",");
+ pw.print(ps.getInstalled(user.id) ? "I" : "i");
+ pw.print(ps.getBlocked(user.id) ? "B" : "b");
+ pw.print(ps.getStopped(user.id) ? "S" : "s");
+ pw.print(ps.getNotLaunched(user.id) ? "l" : "L");
+ pw.print(",");
+ pw.print(ps.getEnabled(user.id));
+ String lastDisabledAppCaller = ps.getLastDisabledAppCaller(user.id);
+ pw.print(",");
+ pw.print(lastDisabledAppCaller != null ? lastDisabledAppCaller : "?");
+ pw.println();
+ }
+ return;
+ }
+
pw.print(prefix); pw.print("Package [");
pw.print(ps.realName != null ? ps.realName : ps.name);
pw.print("] (");
@@ -3055,7 +3145,7 @@ final class Settings {
}
}
- void dumpPackagesLPr(PrintWriter pw, String packageName, DumpState dumpState) {
+ void dumpPackagesLPr(PrintWriter pw, String packageName, DumpState dumpState, boolean checkin) {
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
final Date date = new Date();
boolean printedSomething = false;
@@ -3066,35 +3156,39 @@ final class Settings {
continue;
}
- if (packageName != null) {
+ if (!checkin && packageName != null) {
dumpState.setSharedUser(ps.sharedUser);
}
- if (!printedSomething) {
+ if (!checkin && !printedSomething) {
if (dumpState.onTitlePrinted())
pw.println();
pw.println("Packages:");
printedSomething = true;
}
- dumpPackageLPr(pw, " ", ps, sdf, date, users);
+ dumpPackageLPr(pw, " ", checkin ? "pkg" : null, ps, sdf, date, users);
}
printedSomething = false;
- if (mRenamedPackages.size() > 0) {
+ if (!checkin && mRenamedPackages.size() > 0) {
for (final Map.Entry<String, String> e : mRenamedPackages.entrySet()) {
if (packageName != null && !packageName.equals(e.getKey())
&& !packageName.equals(e.getValue())) {
continue;
}
- if (!printedSomething) {
- if (dumpState.onTitlePrinted())
- pw.println();
- pw.println("Renamed packages:");
- printedSomething = true;
+ if (!checkin) {
+ if (!printedSomething) {
+ if (dumpState.onTitlePrinted())
+ pw.println();
+ pw.println("Renamed packages:");
+ printedSomething = true;
+ }
+ pw.print(" ");
+ } else {
+ pw.print("ren,");
}
- pw.print(" ");
pw.print(e.getKey());
- pw.print(" -> ");
+ pw.print(checkin ? " -> " : ",");
pw.println(e.getValue());
}
}
@@ -3106,13 +3200,13 @@ final class Settings {
&& !packageName.equals(ps.name)) {
continue;
}
- if (!printedSomething) {
+ if (!checkin && !printedSomething) {
if (dumpState.onTitlePrinted())
pw.println();
pw.println("Hidden system packages:");
printedSomething = true;
}
- dumpPackageLPr(pw, " ", ps, sdf, date, users);
+ dumpPackageLPr(pw, " ", checkin ? "dis" : null, ps, sdf, date, users);
}
}
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index c33134a..fc98c4e 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -92,6 +92,7 @@ public class UserManagerService extends IUserManager.Stub {
private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber";
private static final String ATTR_PARTIAL = "partial";
private static final String ATTR_USER_VERSION = "version";
+ private static final String ATTR_RELATED_GROUP_ID = "relatedGroupId";
private static final String TAG_USERS = "users";
private static final String TAG_USER = "user";
private static final String TAG_RESTRICTIONS = "restrictions";
@@ -258,6 +259,29 @@ public class UserManagerService extends IUserManager.Stub {
}
@Override
+ public List<UserInfo> getRelatedUsers(int userId) {
+ checkManageUsersPermission("query users");
+ synchronized (mPackagesLock) {
+ UserInfo user = getUserInfoLocked(userId);
+ ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
+ for (int i = 0; i < mUsers.size(); i++) {
+ UserInfo ui = mUsers.valueAt(i);
+ if (!areRelatedUsers(user, ui)) {
+ continue;
+ }
+ users.add(ui);
+ }
+ return users;
+ }
+ }
+
+ private boolean areRelatedUsers(UserInfo user1, UserInfo user2) {
+ return user1.relatedGroupId != UserInfo.NO_RELATED_GROUP_ID &&
+ user1.relatedGroupId == user2.relatedGroupId &&
+ user1.id != user2.id;
+ }
+
+ @Override
public UserInfo getUserInfo(int userId) {
checkManageUsersPermission("query user");
synchronized (mPackagesLock) {
@@ -662,6 +686,10 @@ public class UserManagerService extends IUserManager.Stub {
if (userInfo.partial) {
serializer.attribute(null, ATTR_PARTIAL, "true");
}
+ if (userInfo.relatedGroupId != UserInfo.NO_RELATED_GROUP_ID) {
+ serializer.attribute(null, ATTR_RELATED_GROUP_ID,
+ Integer.toString(userInfo.relatedGroupId));
+ }
serializer.startTag(null, TAG_NAME);
serializer.text(userInfo.name);
@@ -745,6 +773,7 @@ public class UserManagerService extends IUserManager.Stub {
long salt = 0L;
String pinHash = null;
int failedAttempts = 0;
+ int relatedGroupId = UserInfo.NO_RELATED_GROUP_ID;
long lastAttemptTime = 0L;
boolean partial = false;
Bundle restrictions = new Bundle();
@@ -782,6 +811,8 @@ public class UserManagerService extends IUserManager.Stub {
pinHash = parser.getAttributeValue(null, ATTR_PIN_HASH);
failedAttempts = readIntAttribute(parser, ATTR_FAILED_ATTEMPTS, 0);
lastAttemptTime = readLongAttribute(parser, ATTR_LAST_RETRY_MS, 0L);
+ relatedGroupId = readIntAttribute(parser, ATTR_RELATED_GROUP_ID,
+ UserInfo.NO_RELATED_GROUP_ID);
String valueString = parser.getAttributeValue(null, ATTR_PARTIAL);
if ("true".equals(valueString)) {
partial = true;
@@ -820,6 +851,7 @@ public class UserManagerService extends IUserManager.Stub {
userInfo.creationTime = creationTime;
userInfo.lastLoggedInTime = lastLoggedInTime;
userInfo.partial = partial;
+ userInfo.relatedGroupId = relatedGroupId;
mUserRestrictions.append(id, restrictions);
if (salt != 0L) {
RestrictionsPinState pinState = mRestrictionsPinStates.get(id);
@@ -934,15 +966,44 @@ public class UserManagerService extends IUserManager.Stub {
}
}
+ private int getNextRelatedGroupIdLocked() {
+ int maxGroupId = UserInfo.NO_RELATED_GROUP_ID;
+ for (int i = 0; i < mUsers.size(); i++) {
+ UserInfo ui = mUsers.valueAt(i);
+ if (maxGroupId < ui.relatedGroupId) {
+ maxGroupId = ui.relatedGroupId;
+ }
+ }
+ return maxGroupId + 1;
+ }
+
+ @Override
+ public UserInfo createRelatedUser(String name, int flags, int relatedUserId) {
+ checkManageUsersPermission("Only the system can create users");
+ if (relatedUserId != UserHandle.USER_OWNER) {
+ Slog.w(LOG_TAG, "Only user owner can have related users");
+ return null;
+ }
+ return createUserInternal(name, flags, relatedUserId);
+ }
+
@Override
public UserInfo createUser(String name, int flags) {
checkManageUsersPermission("Only the system can create users");
+ return createUserInternal(name, flags, UserHandle.USER_NULL);
+ }
+ private UserInfo createUserInternal(String name, int flags, int relatedUserId) {
final long ident = Binder.clearCallingIdentity();
- final UserInfo userInfo;
+ UserInfo userInfo = null;
try {
synchronized (mInstallLock) {
synchronized (mPackagesLock) {
+ UserInfo relatedUser = null;
+ if (relatedUserId != UserHandle.USER_NULL) {
+ relatedUser = getUserInfoLocked(relatedUserId);
+ if (relatedUser == null) return null;
+ }
if (isUserLimitReachedLocked()) return null;
int userId = getNextAvailableIdLocked();
userInfo = new UserInfo(userId, name, null, flags);
@@ -954,6 +1015,13 @@ public class UserManagerService extends IUserManager.Stub {
Environment.getUserSystemDirectory(userInfo.id).mkdirs();
mUsers.put(userId, userInfo);
writeUserListLocked();
+ if (relatedUser != null) {
+ if (relatedUser.relatedGroupId == UserInfo.NO_RELATED_GROUP_ID) {
+ relatedUser.relatedGroupId = getNextRelatedGroupIdLocked();
+ }
+ userInfo.relatedGroupId = relatedUser.relatedGroupId;
+ writeUserLocked(relatedUser);
+ }
writeUserLocked(userInfo);
mPm.createNewUserLILPw(userId, userPath);
userInfo.partial = false;
diff --git a/services/core/java/com/android/server/power/AutomaticBrightnessController.java b/services/core/java/com/android/server/power/AutomaticBrightnessController.java
new file mode 100644
index 0000000..3ca628a
--- /dev/null
+++ b/services/core/java/com/android/server/power/AutomaticBrightnessController.java
@@ -0,0 +1,687 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import com.android.server.twilight.TwilightListener;
+import com.android.server.twilight.TwilightManager;
+import com.android.server.twilight.TwilightState;
+
+import android.content.res.Resources;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.text.format.DateUtils;
+import android.util.MathUtils;
+import android.util.Spline;
+import android.util.Slog;
+import android.util.TimeUtils;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+class AutomaticBrightnessController {
+ private static final String TAG = "AutomaticBrightnessController";
+
+ private static final boolean DEBUG = false;
+ private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
+
+ // If true, enables the use of the screen auto-brightness adjustment setting.
+ private static final boolean USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT =
+ PowerManager.useScreenAutoBrightnessAdjustmentFeature();
+
+ // The maximum range of gamma adjustment possible using the screen
+ // auto-brightness adjustment setting.
+ private static final float SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA = 3.0f;
+
+ // Light sensor event rate in milliseconds.
+ private static final int LIGHT_SENSOR_RATE_MILLIS = 1000;
+
+ // Period of time in which to consider light samples in milliseconds.
+ private static final int AMBIENT_LIGHT_HORIZON = 10000;
+
+ // Stability requirements in milliseconds for accepting a new brightness level. This is used
+ // for debouncing the light sensor. Different constants are used to debounce the light sensor
+ // when adapting to brighter or darker environments. This parameter controls how quickly
+ // brightness changes occur in response to an observed change in light level that exceeds the
+ // hysteresis threshold.
+ private static final long BRIGHTENING_LIGHT_DEBOUNCE = 4000;
+ private static final long DARKENING_LIGHT_DEBOUNCE = 8000;
+
+ // Hysteresis constraints for brightening or darkening.
+ // The recent lux must have changed by at least this fraction relative to the
+ // current ambient lux before a change will be considered.
+ private static final float BRIGHTENING_LIGHT_HYSTERESIS = 0.10f;
+ private static final float DARKENING_LIGHT_HYSTERESIS = 0.20f;
+
+ // The intercept used for the weighting calculation. This is used in order to keep all possible
+ // weighting values positive.
+ private static final int WEIGHTING_INTERCEPT = AMBIENT_LIGHT_HORIZON;
+
+ // How long the current sensor reading is assumed to be valid beyond the current time.
+ // This provides a bit of prediction, as well as ensures that the weight for the last sample is
+ // non-zero, which in turn ensures that the total weight is non-zero.
+ private static final long AMBIENT_LIGHT_PREDICTION_TIME_MILLIS = 100;
+
+ // If true, enables the use of the current time as an auto-brightness adjustment.
+ // The basic idea here is to expand the dynamic range of auto-brightness
+ // when it is especially dark outside. The light sensor tends to perform
+ // poorly at low light levels so we compensate for it by making an
+ // assumption about the environment.
+ private static final boolean USE_TWILIGHT_ADJUSTMENT =
+ PowerManager.useTwilightAdjustmentFeature();
+
+ // Specifies the maximum magnitude of the time of day adjustment.
+ private static final float TWILIGHT_ADJUSTMENT_MAX_GAMMA = 1.5f;
+
+ // The amount of time after or before sunrise over which to start adjusting
+ // the gamma. We want the change to happen gradually so that it is below the
+ // threshold of perceptibility and so that the adjustment has maximum effect
+ // well after dusk.
+ private static final long TWILIGHT_ADJUSTMENT_TIME = DateUtils.HOUR_IN_MILLIS * 2;
+
+ private static final int MSG_UPDATE_AMBIENT_LUX = 1;
+
+ // Callbacks for requesting updates to the the display's power state
+ private final Callbacks mCallbacks;
+
+ // The sensor manager.
+ private final SensorManager mSensorManager;
+
+ // The light sensor, or null if not available or needed.
+ private final Sensor mLightSensor;
+
+ // The twilight service.
+ private final TwilightManager mTwilight;
+
+ // The auto-brightness spline adjustment.
+ // The brightness values have been scaled to a range of 0..1.
+ private final Spline mScreenAutoBrightnessSpline;
+
+ // The minimum and maximum screen brightnesses.
+ private final int mScreenBrightnessRangeMinimum;
+ private final int mScreenBrightnessRangeMaximum;
+
+ // Amount of time to delay auto-brightness after screen on while waiting for
+ // the light sensor to warm-up in milliseconds.
+ // May be 0 if no warm-up is required.
+ private int mLightSensorWarmUpTimeConfig;
+
+ // Set to true if the light sensor is enabled.
+ private boolean mLightSensorEnabled;
+
+ // The time when the light sensor was enabled.
+ private long mLightSensorEnableTime;
+
+ // The currently accepted nominal ambient light level.
+ private float mAmbientLux;
+
+ // True if mAmbientLux holds a valid value.
+ private boolean mAmbientLuxValid;
+
+ // The ambient light level threshold at which to brighten or darken the screen.
+ private float mBrighteningLuxThreshold;
+ private float mDarkeningLuxThreshold;
+
+ // The most recent light sample.
+ private float mLastObservedLux;
+
+ // The time of the most light recent sample.
+ private long mLastObservedLuxTime;
+
+ // The number of light samples collected since the light sensor was enabled.
+ private int mRecentLightSamples;
+
+ // A ring buffer containing all of the recent ambient light sensor readings.
+ private AmbientLightRingBuffer mAmbientLightRingBuffer;
+
+ // The handler
+ private AutomaticBrightnessHandler mHandler;
+
+ // The screen brightness level that has been chosen by the auto-brightness
+ // algorithm. The actual brightness should ramp towards this value.
+ // We preserve this value even when we stop using the light sensor so
+ // that we can quickly revert to the previous auto-brightness level
+ // while the light sensor warms up.
+ // Use -1 if there is no current auto-brightness value available.
+ private int mScreenAutoBrightness = -1;
+
+ // The screen auto-brightness adjustment factor in the range -1 (dimmer) to 1 (brighter)
+ private float mScreenAutoBrightnessAdjustment = 0.0f;
+
+ // The last screen auto-brightness gamma. (For printing in dump() only.)
+ private float mLastScreenAutoBrightnessGamma = 1.0f;
+
+ public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
+ TwilightManager twilight, SensorManager sensorManager, Spline autoBrightnessSpline,
+ int lightSensorWarmUpTime, int brightnessMin, int brightnessMax) {
+ mCallbacks = callbacks;
+ mTwilight = twilight;
+ mSensorManager = sensorManager;
+ mScreenAutoBrightnessSpline = autoBrightnessSpline;
+ mScreenBrightnessRangeMinimum = brightnessMin;
+ mScreenBrightnessRangeMaximum = brightnessMax;
+ mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime;
+
+ mHandler = new AutomaticBrightnessHandler(looper);
+ mAmbientLightRingBuffer = new AmbientLightRingBuffer();
+
+ if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
+ mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
+ }
+
+ if (USE_TWILIGHT_ADJUSTMENT) {
+ mTwilight.registerListener(mTwilightListener, mHandler);
+ }
+ }
+
+ public int getAutomaticScreenBrightness() {
+ return mScreenAutoBrightness;
+ }
+
+ public void updatePowerState(DisplayPowerRequest request) {
+ if (setScreenAutoBrightnessAdjustment(request.screenAutoBrightnessAdjustment)
+ || setLightSensorEnabled(request.useAutoBrightness
+ && request.wantScreenOnNormal())) {
+ updateAutoBrightness(false /*sendUpdate*/);
+ }
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println();
+ pw.println("Automatic Brightness Controller Configuration:");
+ pw.println(" mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline);
+ pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
+ pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
+ pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
+
+ pw.println();
+ pw.println("Automatic Brightness Controller State:");
+ pw.println(" mLightSensor=" + mLightSensor);
+ pw.println(" mTwilight.getCurrentState()=" + mTwilight.getCurrentState());
+ pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
+ pw.println(" mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
+ pw.println(" mAmbientLux=" + mAmbientLux);
+ pw.println(" mBrighteningLuxThreshold=" + mBrighteningLuxThreshold);
+ pw.println(" mDarkeningLuxThreshold=" + mDarkeningLuxThreshold);
+ pw.println(" mLastObservedLux=" + mLastObservedLux);
+ pw.println(" mLastObservedLuxTime=" + TimeUtils.formatUptime(mLastObservedLuxTime));
+ pw.println(" mRecentLightSamples=" + mRecentLightSamples);
+ pw.println(" mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
+ pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
+ pw.println(" mScreenAutoBrightnessAdjustment=" + mScreenAutoBrightnessAdjustment);
+ pw.println(" mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma);
+ }
+
+ private boolean setLightSensorEnabled(boolean enable) {
+ if (enable) {
+ if (!mLightSensorEnabled) {
+ mLightSensorEnabled = true;
+ mLightSensorEnableTime = SystemClock.uptimeMillis();
+ mSensorManager.registerListener(mLightSensorListener, mLightSensor,
+ LIGHT_SENSOR_RATE_MILLIS * 1000, mHandler);
+ return true;
+ }
+ } else {
+ if (mLightSensorEnabled) {
+ mLightSensorEnabled = false;
+ mAmbientLuxValid = false;
+ mRecentLightSamples = 0;
+ mAmbientLightRingBuffer.clear();
+ mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
+ mSensorManager.unregisterListener(mLightSensorListener);
+ }
+ }
+ return false;
+ }
+
+ private void handleLightSensorEvent(long time, float lux) {
+ mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
+
+ applyLightSensorMeasurement(time, lux);
+ updateAmbientLux(time);
+ }
+
+ private void applyLightSensorMeasurement(long time, float lux) {
+ mRecentLightSamples++;
+ mAmbientLightRingBuffer.prune(time - AMBIENT_LIGHT_HORIZON);
+ mAmbientLightRingBuffer.push(time, lux);
+
+ // Remember this sample value.
+ mLastObservedLux = lux;
+ mLastObservedLuxTime = time;
+ }
+
+ private boolean setScreenAutoBrightnessAdjustment(float adjustment) {
+ if (adjustment != mScreenAutoBrightnessAdjustment) {
+ mScreenAutoBrightnessAdjustment = adjustment;
+ return true;
+ }
+ return false;
+ }
+
+ private void setAmbientLux(float lux) {
+ mAmbientLux = lux;
+ mBrighteningLuxThreshold = mAmbientLux * (1.0f + BRIGHTENING_LIGHT_HYSTERESIS);
+ mDarkeningLuxThreshold = mAmbientLux * (1.0f - DARKENING_LIGHT_HYSTERESIS);
+ }
+
+ private float calculateAmbientLux(long now) {
+ final int N = mAmbientLightRingBuffer.size();
+ if (N == 0) {
+ Slog.e(TAG, "calculateAmbientLux: No ambient light readings available");
+ return -1;
+ }
+ float sum = 0;
+ float totalWeight = 0;
+ long endTime = AMBIENT_LIGHT_PREDICTION_TIME_MILLIS;
+ for (int i = N - 1; i >= 0; i--) {
+ long startTime = (mAmbientLightRingBuffer.getTime(i) - now);
+ float weight = calculateWeight(startTime, endTime);
+ float lux = mAmbientLightRingBuffer.getLux(i);
+ if (DEBUG) {
+ Slog.d(TAG, "calculateAmbientLux: [" +
+ (startTime) + ", " +
+ (endTime) + "]: lux=" + lux + ", weight=" + weight);
+ }
+ totalWeight += weight;
+ sum += mAmbientLightRingBuffer.getLux(i) * weight;
+ endTime = startTime;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "calculateAmbientLux: totalWeight=" + totalWeight +
+ ", newAmbientLux=" + (sum / totalWeight));
+ }
+ return sum / totalWeight;
+ }
+
+ private static float calculateWeight(long startDelta, long endDelta) {
+ return weightIntegral(endDelta) - weightIntegral(startDelta);
+ }
+
+ // Evaluates the integral of y = x + WEIGHTING_INTERCEPT. This is always positive for the
+ // horizon we're looking at and provides a non-linear weighting for light samples.
+ private static float weightIntegral(long x) {
+ return x * (x * 0.5f + WEIGHTING_INTERCEPT);
+ }
+
+ private long nextAmbientLightBrighteningTransition(long time) {
+ final int N = mAmbientLightRingBuffer.size();
+ long earliestValidTime = time;
+ for (int i = N - 1; i >= 0; i--) {
+ if (mAmbientLightRingBuffer.getLux(i) <= mBrighteningLuxThreshold) {
+ break;
+ }
+ earliestValidTime = mAmbientLightRingBuffer.getTime(i);
+ }
+ return earliestValidTime + BRIGHTENING_LIGHT_DEBOUNCE;
+ }
+
+ private long nextAmbientLightDarkeningTransition(long time) {
+ final int N = mAmbientLightRingBuffer.size();
+ long earliestValidTime = time;
+ for (int i = N - 1; i >= 0; i--) {
+ if (mAmbientLightRingBuffer.getLux(i) >= mDarkeningLuxThreshold) {
+ break;
+ }
+ earliestValidTime = mAmbientLightRingBuffer.getTime(i);
+ }
+ return earliestValidTime + DARKENING_LIGHT_DEBOUNCE;
+ }
+
+ private void updateAmbientLux() {
+ long time = SystemClock.uptimeMillis();
+ mAmbientLightRingBuffer.prune(time - AMBIENT_LIGHT_HORIZON);
+ updateAmbientLux(time);
+ }
+
+ private void updateAmbientLux(long time) {
+ // If the light sensor was just turned on then immediately update our initial
+ // estimate of the current ambient light level.
+ if (!mAmbientLuxValid) {
+ final long timeWhenSensorWarmedUp =
+ mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
+ if (time < timeWhenSensorWarmedUp) {
+ if (DEBUG) {
+ Slog.d(TAG, "updateAmbientLux: Sensor not ready yet: "
+ + "time=" + time
+ + ", timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp);
+ }
+ mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX,
+ timeWhenSensorWarmedUp);
+ return;
+ }
+ setAmbientLux(calculateAmbientLux(time));
+ mAmbientLuxValid = true;
+ if (DEBUG) {
+ Slog.d(TAG, "updateAmbientLux: Initializing: "
+ + "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer
+ + ", mAmbientLux=" + mAmbientLux);
+ }
+ updateAutoBrightness(true);
+ }
+
+ long nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
+ long nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
+ float ambientLux = calculateAmbientLux(time);
+
+ if (ambientLux >= mBrighteningLuxThreshold && nextBrightenTransition <= time
+ || ambientLux <= mDarkeningLuxThreshold && nextDarkenTransition <= time) {
+ setAmbientLux(ambientLux);
+ if (DEBUG) {
+ Slog.d(TAG, "updateAmbientLux: "
+ + ((ambientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
+ + "mBrighteningLuxThreshold=" + mBrighteningLuxThreshold
+ + ", mAmbientLightRingBuffer=" + mAmbientLightRingBuffer
+ + ", mAmbientLux=" + mAmbientLux);
+ }
+ updateAutoBrightness(true);
+ nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
+ nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
+ }
+ long nextTransitionTime = Math.min(nextDarkenTransition, nextBrightenTransition);
+ // If one of the transitions is ready to occur, but the total weighted ambient lux doesn't
+ // exceed the necessary threshold, then it's possible we'll get a transition time prior to
+ // now. Rather than continually checking to see whether the weighted lux exceeds the
+ // threshold, schedule an update for when we'd normally expect another light sample, which
+ // should be enough time to decide whether we should actually transition to the new
+ // weighted ambient lux or not.
+ nextTransitionTime =
+ nextTransitionTime > time ? nextTransitionTime : time + LIGHT_SENSOR_RATE_MILLIS;
+ if (DEBUG) {
+ Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for "
+ + nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
+ }
+ mHandler.sendEmptyMessageAtTime(MSG_UPDATE_AMBIENT_LUX, nextTransitionTime);
+ }
+
+ private void updateAutoBrightness(boolean sendUpdate) {
+ if (!mAmbientLuxValid) {
+ return;
+ }
+
+ float value = mScreenAutoBrightnessSpline.interpolate(mAmbientLux);
+ float gamma = 1.0f;
+
+ if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT
+ && mScreenAutoBrightnessAdjustment != 0.0f) {
+ final float adjGamma = MathUtils.pow(SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA,
+ Math.min(1.0f, Math.max(-1.0f, -mScreenAutoBrightnessAdjustment)));
+ gamma *= adjGamma;
+ if (DEBUG) {
+ Slog.d(TAG, "updateAutoBrightness: adjGamma=" + adjGamma);
+ }
+ }
+
+ if (USE_TWILIGHT_ADJUSTMENT) {
+ TwilightState state = mTwilight.getCurrentState();
+ if (state != null && state.isNight()) {
+ final long now = System.currentTimeMillis();
+ final float earlyGamma =
+ getTwilightGamma(now, state.getYesterdaySunset(), state.getTodaySunrise());
+ final float lateGamma =
+ getTwilightGamma(now, state.getTodaySunset(), state.getTomorrowSunrise());
+ gamma *= earlyGamma * lateGamma;
+ if (DEBUG) {
+ Slog.d(TAG, "updateAutoBrightness: earlyGamma=" + earlyGamma
+ + ", lateGamma=" + lateGamma);
+ }
+ }
+ }
+
+ if (gamma != 1.0f) {
+ final float in = value;
+ value = MathUtils.pow(value, gamma);
+ if (DEBUG) {
+ Slog.d(TAG, "updateAutoBrightness: gamma=" + gamma
+ + ", in=" + in + ", out=" + value);
+ }
+ }
+
+ int newScreenAutoBrightness =
+ clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
+ if (mScreenAutoBrightness != newScreenAutoBrightness) {
+ if (DEBUG) {
+ Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness="
+ + mScreenAutoBrightness + ", newScreenAutoBrightness="
+ + newScreenAutoBrightness);
+ }
+
+ mScreenAutoBrightness = newScreenAutoBrightness;
+ mLastScreenAutoBrightnessGamma = gamma;
+ if (sendUpdate) {
+ mCallbacks.updateBrightness();
+ }
+ }
+ }
+
+ private int clampScreenBrightness(int value) {
+ return MathUtils.constrain(value,
+ mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
+ }
+
+ private static float getTwilightGamma(long now, long lastSunset, long nextSunrise) {
+ if (lastSunset < 0 || nextSunrise < 0
+ || now < lastSunset || now > nextSunrise) {
+ return 1.0f;
+ }
+
+ if (now < lastSunset + TWILIGHT_ADJUSTMENT_TIME) {
+ return MathUtils.lerp(1.0f, TWILIGHT_ADJUSTMENT_MAX_GAMMA,
+ (float)(now - lastSunset) / TWILIGHT_ADJUSTMENT_TIME);
+ }
+
+ if (now > nextSunrise - TWILIGHT_ADJUSTMENT_TIME) {
+ return MathUtils.lerp(1.0f, TWILIGHT_ADJUSTMENT_MAX_GAMMA,
+ (float)(nextSunrise - now) / TWILIGHT_ADJUSTMENT_TIME);
+ }
+
+ return TWILIGHT_ADJUSTMENT_MAX_GAMMA;
+ }
+
+ private final class AutomaticBrightnessHandler extends Handler {
+ public AutomaticBrightnessHandler(Looper looper) {
+ super(looper, null, true /*async*/);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_AMBIENT_LUX:
+ updateAmbientLux();
+ break;
+ }
+ }
+ }
+
+ private final SensorEventListener mLightSensorListener = new SensorEventListener() {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (mLightSensorEnabled) {
+ final long time = SystemClock.uptimeMillis();
+ final float lux = event.values[0];
+ handleLightSensorEvent(time, lux);
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // Not used.
+ }
+ };
+
+ private final TwilightListener mTwilightListener = new TwilightListener() {
+ @Override
+ public void onTwilightStateChanged() {
+ updateAutoBrightness(true /*sendUpdate*/);
+ }
+ };
+
+ /** Callbacks to request updates to the display's power state. */
+ interface Callbacks {
+ void updateBrightness();
+ }
+
+ private static final class AmbientLightRingBuffer{
+ // Proportional extra capacity of the buffer beyond the expected number of light samples
+ // in the horizon
+ private static final float BUFFER_SLACK = 1.5f;
+ private static final int DEFAULT_CAPACITY =
+ (int) Math.ceil(AMBIENT_LIGHT_HORIZON * BUFFER_SLACK / LIGHT_SENSOR_RATE_MILLIS);
+ private float[] mRingLux;
+ private long[] mRingTime;
+ private int mCapacity;
+
+ // The first valid element and the next open slot.
+ // Note that if mCount is zero then there are no valid elements.
+ private int mStart;
+ private int mEnd;
+ private int mCount;
+
+ public AmbientLightRingBuffer() {
+ this(DEFAULT_CAPACITY);
+ }
+
+ public AmbientLightRingBuffer(int initialCapacity) {
+ mCapacity = initialCapacity;
+ mRingLux = new float[mCapacity];
+ mRingTime = new long[mCapacity];
+ }
+
+ public float getLux(int index) {
+ return mRingLux[offsetOf(index)];
+ }
+
+ public long getTime(int index) {
+ return mRingTime[offsetOf(index)];
+ }
+
+ public void push(long time, float lux) {
+ int next = mEnd;
+ if (mCount == mCapacity) {
+ int newSize = mCapacity * 2;
+
+ float[] newRingLux = new float[newSize];
+ long[] newRingTime = new long[newSize];
+ int length = mCapacity - mStart;
+ System.arraycopy(mRingLux, mStart, newRingLux, 0, length);
+ System.arraycopy(mRingTime, mStart, newRingTime, 0, length);
+ if (mStart != 0) {
+ System.arraycopy(mRingLux, 0, newRingLux, length, mStart);
+ System.arraycopy(mRingTime, 0, newRingTime, length, mStart);
+ }
+ mRingLux = newRingLux;
+ mRingTime = newRingTime;
+
+ next = mCapacity;
+ mCapacity = newSize;
+ mStart = 0;
+ }
+ mRingTime[next] = time;
+ mRingLux[next] = lux;
+ mEnd = next + 1;
+ if (mEnd == mCapacity) {
+ mEnd = 0;
+ }
+ mCount++;
+ }
+
+ public void prune(long horizon) {
+ if (mCount == 0) {
+ return;
+ }
+
+ while (mCount > 1) {
+ int next = mStart + 1;
+ if (next >= mCapacity) {
+ next -= mCapacity;
+ }
+ if (mRingTime[next] > horizon) {
+ // Some light sensors only produce data upon a change in the ambient light
+ // levels, so we need to consider the previous measurement as the ambient light
+ // level for all points in time up until we receive a new measurement. Thus, we
+ // always want to keep the youngest element that would be removed from the
+ // buffer and just set its measurement time to the horizon time since at that
+ // point it is the ambient light level, and to remove it would be to drop a
+ // valid data point within our horizon.
+ break;
+ }
+ mStart = next;
+ mCount -= 1;
+ }
+
+ if (mRingTime[mStart] < horizon) {
+ mRingTime[mStart] = horizon;
+ }
+ }
+
+ public int size() {
+ return mCount;
+ }
+
+ public boolean isEmpty() {
+ return mCount == 0;
+ }
+
+ public void clear() {
+ mStart = 0;
+ mEnd = 0;
+ mCount = 0;
+ }
+
+ @Override
+ public String toString() {
+ final int length = mCapacity - mStart;
+ float[] lux = new float[mCount];
+ long[] time = new long[mCount];
+
+ if (mCount <= length) {
+ System.arraycopy(mRingLux, mStart, lux, 0, mCount);
+ System.arraycopy(mRingTime, mStart, time, 0, mCount);
+ } else {
+ System.arraycopy(mRingLux, mStart, lux, 0, length);
+ System.arraycopy(mRingLux, 0, lux, length, mCount - length);
+
+ System.arraycopy(mRingTime, mStart, time, 0, length);
+ System.arraycopy(mRingTime, 0, time, length, mCount - length);
+ }
+ return "AmbientLightRingBuffer{mCapacity=" + mCapacity
+ + ", mStart=" + mStart
+ + ", mEnd=" + mEnd
+ + ", mCount=" + mCount
+ + ", mRingLux=" + Arrays.toString(lux)
+ + ", mRingTime=" + Arrays.toString(time)
+ + "}";
+ }
+
+ private int offsetOf(int index) {
+ if (index >= mCount || index < 0) {
+ throw new ArrayIndexOutOfBoundsException(index);
+ }
+ index += mStart;
+ if (index >= mCapacity) {
+ index -= mCapacity;
+ }
+ return index;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/DisplayPowerController.java b/services/core/java/com/android/server/power/DisplayPowerController.java
index bd9acb9..12d51aa 100644
--- a/services/core/java/com/android/server/power/DisplayPowerController.java
+++ b/services/core/java/com/android/server/power/DisplayPowerController.java
@@ -35,7 +35,7 @@ import android.os.Message;
import android.os.PowerManager;
import android.os.SystemClock;
import android.text.format.DateUtils;
-import android.util.FloatMath;
+import android.util.MathUtils;
import android.util.Slog;
import android.util.Spline;
import android.util.TimeUtils;
@@ -64,12 +64,11 @@ import java.io.PrintWriter;
* For debugging, you can make the electron beam and brightness animations run
* slower by changing the "animator duration scale" option in Development Settings.
*/
-final class DisplayPowerController {
+final class DisplayPowerController implements AutomaticBrightnessController.Callbacks {
private static final String TAG = "DisplayPowerController";
private static boolean DEBUG = false;
private static final boolean DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT = false;
- private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
// If true, uses the electron beam on animation.
// We might want to turn this off if we cannot get a guarantee that the screen
@@ -77,13 +76,6 @@ final class DisplayPowerController {
// screen state returns. Playing the animation can also be somewhat slow.
private static final boolean USE_ELECTRON_BEAM_ON_ANIMATION = false;
- // If true, enables the use of the screen auto-brightness adjustment setting.
- private static final boolean USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT =
- PowerManager.useScreenAutoBrightnessAdjustmentFeature();
-
- // The maximum range of gamma adjustment possible using the screen
- // auto-brightness adjustment setting.
- private static final float SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA = 3.0f;
// The minimum reduction in brightness when dimmed.
private static final int SCREEN_DIM_MINIMUM_REDUCTION = 10;
@@ -110,7 +102,6 @@ final class DisplayPowerController {
private static final int MSG_UPDATE_POWER_STATE = 1;
private static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 2;
- private static final int MSG_LIGHT_SENSOR_DEBOUNCED = 3;
private static final int PROXIMITY_UNKNOWN = -1;
private static final int PROXIMITY_NEGATIVE = 0;
@@ -123,41 +114,10 @@ final class DisplayPowerController {
// Trigger proximity if distance is less than 5 cm.
private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f;
- // Light sensor event rate in milliseconds.
- private static final int LIGHT_SENSOR_RATE_MILLIS = 1000;
-
- // A rate for generating synthetic light sensor events in the case where the light
- // sensor hasn't reported any new data in a while and we need it to update the
- // debounce filter. We only synthesize light sensor measurements when needed.
- private static final int SYNTHETIC_LIGHT_SENSOR_RATE_MILLIS =
- LIGHT_SENSOR_RATE_MILLIS * 2;
-
// Brightness animation ramp rate in brightness units per second.
private static final int BRIGHTNESS_RAMP_RATE_FAST = 200;
private static final int BRIGHTNESS_RAMP_RATE_SLOW = 40;
- // IIR filter time constants in milliseconds for computing two moving averages of
- // the light samples. One is a long-term average and the other is a short-term average.
- // We can use these filters to assess trends in ambient brightness.
- // The short term average gives us a filtered but relatively low latency measurement.
- // The long term average informs us about the overall trend.
- private static final long SHORT_TERM_AVERAGE_LIGHT_TIME_CONSTANT = 1000;
- private static final long LONG_TERM_AVERAGE_LIGHT_TIME_CONSTANT = 5000;
-
- // Stability requirements in milliseconds for accepting a new brightness
- // level. This is used for debouncing the light sensor. Different constants
- // are used to debounce the light sensor when adapting to brighter or darker environments.
- // This parameter controls how quickly brightness changes occur in response to
- // an observed change in light level that exceeds the hysteresis threshold.
- private static final long BRIGHTENING_LIGHT_DEBOUNCE = 4000;
- private static final long DARKENING_LIGHT_DEBOUNCE = 8000;
-
- // Hysteresis constraints for brightening or darkening.
- // The recent lux must have changed by at least this fraction relative to the
- // current ambient lux before a change will be considered.
- private static final float BRIGHTENING_LIGHT_HYSTERESIS = 0.10f;
- private static final float DARKENING_LIGHT_HYSTERESIS = 0.20f;
-
private final Object mLock = new Object();
// Notifier for sending asynchronous notifications.
@@ -181,18 +141,12 @@ final class DisplayPowerController {
// The lights service.
private final LightsManager mLights;
- // The twilight service.
- private final TwilightManager mTwilight;
-
// The sensor manager.
private final SensorManager mSensorManager;
// The proximity sensor, or null if not available or needed.
private Sensor mProximitySensor;
- // The light sensor, or null if not available or needed.
- private Sensor mLightSensor;
-
// The doze screen brightness.
private final int mScreenBrightnessDozeConfig;
@@ -208,15 +162,6 @@ final class DisplayPowerController {
// True if auto-brightness should be used.
private boolean mUseSoftwareAutoBrightnessConfig;
- // The auto-brightness spline adjustment.
- // The brightness values have been scaled to a range of 0..1.
- private Spline mScreenAutoBrightnessSpline;
-
- // Amount of time to delay auto-brightness after screen on while waiting for
- // the light sensor to warm-up in milliseconds.
- // May be 0 if no warm-up is required.
- private int mLightSensorWarmUpTimeConfig;
-
// True if we should fade the screen while turning it off, false if we should play
// a stylish electron beam animation instead.
private boolean mElectronBeamFadesConfig;
@@ -286,67 +231,18 @@ final class DisplayPowerController {
// The elapsed real time when the screen on was blocked.
private long mScreenOnBlockStartRealTime;
- // Set to true if the light sensor is enabled.
- private boolean mLightSensorEnabled;
-
- // The time when the light sensor was enabled.
- private long mLightSensorEnableTime;
-
- // The currently accepted nominal ambient light level.
- private float mAmbientLux;
-
- // True if mAmbientLux holds a valid value.
- private boolean mAmbientLuxValid;
-
- // The ambient light level threshold at which to brighten or darken the screen.
- private float mBrighteningLuxThreshold;
- private float mDarkeningLuxThreshold;
-
- // The most recent light sample.
- private float mLastObservedLux;
-
- // The time of the most light recent sample.
- private long mLastObservedLuxTime;
-
- // The number of light samples collected since the light sensor was enabled.
- private int mRecentLightSamples;
-
- // The long-term and short-term filtered light measurements.
- private float mRecentShortTermAverageLux;
- private float mRecentLongTermAverageLux;
-
- // The direction in which the average lux is moving relative to the current ambient lux.
- // 0 if not changing or within hysteresis threshold.
- // 1 if brightening beyond hysteresis threshold.
- // -1 if darkening beyond hysteresis threshold.
- private int mDebounceLuxDirection;
-
- // The time when the average lux last changed direction.
- private long mDebounceLuxTime;
-
- // The screen brightness level that has been chosen by the auto-brightness
- // algorithm. The actual brightness should ramp towards this value.
- // We preserve this value even when we stop using the light sensor so
- // that we can quickly revert to the previous auto-brightness level
- // while the light sensor warms up.
- // Use -1 if there is no current auto-brightness value available.
- private int mScreenAutoBrightness = -1;
-
- // The last screen auto-brightness gamma. (For printing in dump() only.)
- private float mLastScreenAutoBrightnessGamma = 1.0f;
-
// True if the screen auto-brightness value is actually being used to
// set the display brightness.
private boolean mUsingScreenAutoBrightness;
+ // The controller for the automatic brightness level.
+ private AutomaticBrightnessController mAutomaticBrightnessController;
+
// Animators.
private ObjectAnimator mElectronBeamOnAnimator;
private ObjectAnimator mElectronBeamOffAnimator;
private RampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
- // Twilight changed. We might recalculate auto-brightness values.
- private boolean mTwilightChanged;
-
/**
* Creates the display power controller.
*/
@@ -362,7 +258,6 @@ final class DisplayPowerController {
mCallbackHandler = callbackHandler;
mLights = lights;
- mTwilight = twilight;
mSensorManager = sensorManager;
final Resources resources = context.getResources();
@@ -373,9 +268,11 @@ final class DisplayPowerController {
mScreenBrightnessDimConfig = clampAbsoluteBrightness(resources.getInteger(
com.android.internal.R.integer.config_screenBrightnessDim));
- int screenBrightnessMinimum = Math.min(resources.getInteger(
+ int screenBrightnessRangeMinimum = clampAbsoluteBrightness(Math.min(resources.getInteger(
com.android.internal.R.integer.config_screenBrightnessSettingMinimum),
- mScreenBrightnessDimConfig);
+ mScreenBrightnessDimConfig));
+
+ mScreenBrightnessRangeMaximum = PowerManager.BRIGHTNESS_ON;
mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
com.android.internal.R.bool.config_automatic_brightness_available);
@@ -384,9 +281,11 @@ final class DisplayPowerController {
com.android.internal.R.array.config_autoBrightnessLevels);
int[] screenBrightness = resources.getIntArray(
com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
+ int lightSensorWarmUpTimeConfig = resources.getInteger(
+ com.android.internal.R.integer.config_lightSensorWarmupTime);
- mScreenAutoBrightnessSpline = createAutoBrightnessSpline(lux, screenBrightness);
- if (mScreenAutoBrightnessSpline == null) {
+ Spline screenAutoBrightnessSpline = createAutoBrightnessSpline(lux, screenBrightness);
+ if (screenAutoBrightnessSpline == null) {
Slog.e(TAG, "Error in config.xml. config_autoBrightnessLcdBacklightValues "
+ "(size " + screenBrightness.length + ") "
+ "must be monotic and have exactly one more entry than "
@@ -395,17 +294,17 @@ final class DisplayPowerController {
+ "Auto-brightness will be disabled.");
mUseSoftwareAutoBrightnessConfig = false;
} else {
- if (screenBrightness[0] < screenBrightnessMinimum) {
- screenBrightnessMinimum = screenBrightness[0];
+ if (screenBrightness[0] < screenBrightnessRangeMinimum) {
+ screenBrightnessRangeMinimum = clampAbsoluteBrightness(screenBrightness[0]);
}
+ mAutomaticBrightnessController = new AutomaticBrightnessController(this, looper,
+ twilight, sensorManager, screenAutoBrightnessSpline,
+ lightSensorWarmUpTimeConfig, screenBrightnessRangeMinimum,
+ mScreenBrightnessRangeMaximum);
}
-
- mLightSensorWarmUpTimeConfig = resources.getInteger(
- com.android.internal.R.integer.config_lightSensorWarmupTime);
}
- mScreenBrightnessRangeMinimum = clampAbsoluteBrightness(screenBrightnessMinimum);
- mScreenBrightnessRangeMaximum = PowerManager.BRIGHTNESS_ON;
+ mScreenBrightnessRangeMinimum = screenBrightnessRangeMinimum;
mElectronBeamFadesConfig = resources.getBoolean(
com.android.internal.R.bool.config_animateScreenLights);
@@ -418,39 +317,6 @@ final class DisplayPowerController {
}
}
- if (mUseSoftwareAutoBrightnessConfig
- && !DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
- mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
- }
-
- if (mUseSoftwareAutoBrightnessConfig && USE_TWILIGHT_ADJUSTMENT) {
- mTwilight.registerListener(mTwilightListener, mHandler);
- }
- }
-
- private static Spline createAutoBrightnessSpline(int[] lux, int[] brightness) {
- try {
- final int n = brightness.length;
- float[] x = new float[n];
- float[] y = new float[n];
- y[0] = normalizeAbsoluteBrightness(brightness[0]);
- for (int i = 1; i < n; i++) {
- x[i] = lux[i - 1];
- y[i] = normalizeAbsoluteBrightness(brightness[i]);
- }
-
- Spline spline = Spline.createMonotoneCubicSpline(x, y);
- if (DEBUG) {
- Slog.d(TAG, "Auto-brightness spline: " + spline);
- for (float v = 1f; v < lux[lux.length - 1] * 1.25f; v *= 1.25f) {
- Slog.d(TAG, String.format(" %7.1f: %7.1f", v, spline.interpolate(v)));
- }
- }
- return spline;
- } catch (IllegalArgumentException ex) {
- Slog.e(TAG, "Could not create auto-brightness spline.", ex);
- return null;
- }
}
/**
@@ -542,6 +408,14 @@ final class DisplayPowerController {
mScreenBrightnessRampAnimator = new RampAnimator<DisplayPowerState>(
mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS);
+
+ // Initialize screen on state.
+ if (mPowerState.isScreenOn()) {
+ mNotifier.onScreenOn();
+ } else {
+ mNotifier.onScreenOff();
+ }
+ mNotifier.onScreenBrightness(mPowerState.getScreenBrightness());
}
private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() {
@@ -564,9 +438,7 @@ final class DisplayPowerController {
// Update the power state request.
final boolean mustNotify;
boolean mustInitialize = false;
- boolean updateAutoBrightness = mTwilightChanged;
boolean wasDimOrDoze = false;
- mTwilightChanged = false;
synchronized (mLock) {
mPendingUpdatePowerStateLocked = false;
@@ -581,10 +453,6 @@ final class DisplayPowerController {
mPendingRequestChangedLocked = false;
mustInitialize = true;
} else if (mPendingRequestChangedLocked) {
- if (mPowerRequest.screenAutoBrightnessAdjustment
- != mPendingRequestLocked.screenAutoBrightnessAdjustment) {
- updateAutoBrightness = true;
- }
wasDimOrDoze = (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DIM
|| mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DOZE);
mPowerRequest.copyFrom(mPendingRequestLocked);
@@ -632,18 +500,19 @@ final class DisplayPowerController {
}
// Turn on the light sensor if needed.
- if (mLightSensor != null) {
- setLightSensorEnabled(mPowerRequest.wantLightSensorEnabled(),
- updateAutoBrightness);
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.updatePowerState(mPowerRequest);
}
// Set the screen brightness.
if (mPowerRequest.wantScreenOnAny()) {
int target;
boolean slow;
- if (mScreenAutoBrightness >= 0 && mLightSensorEnabled) {
+ int screenAutoBrightness = mAutomaticBrightnessController != null ?
+ mAutomaticBrightnessController.getAutomaticScreenBrightness() : -1;
+ if (screenAutoBrightness >= 0 && mPowerRequest.useAutoBrightness) {
// Use current auto-brightness value.
- target = mScreenAutoBrightness;
+ target = screenAutoBrightness;
slow = mUsingScreenAutoBrightness;
mUsingScreenAutoBrightness = true;
} else {
@@ -754,6 +623,11 @@ final class DisplayPowerController {
}
}
+ @Override
+ public void updateBrightness() {
+ sendUpdatePowerState();
+ }
+
private void blockScreenOn() {
if (!mScreenOnWasBlocked) {
mScreenOnWasBlocked = true;
@@ -786,25 +660,8 @@ final class DisplayPowerController {
}
private int clampScreenBrightness(int value) {
- return clamp(value, mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
- }
-
- private static int clampAbsoluteBrightness(int value) {
- return clamp(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
- }
-
- private static int clamp(int value, int min, int max) {
- if (value <= min) {
- return min;
- }
- if (value >= max) {
- return max;
- }
- return value;
- }
-
- private static float normalizeAbsoluteBrightness(int value) {
- return (float)clampAbsoluteBrightness(value) / PowerManager.BRIGHTNESS_ON;
+ return MathUtils.constrain(
+ value, mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
}
private void animateScreenBrightness(int target, int rate) {
@@ -905,270 +762,6 @@ final class DisplayPowerController {
mPendingProximityDebounceTime = debounceTime;
}
- private void setLightSensorEnabled(boolean enable, boolean updateAutoBrightness) {
- if (enable) {
- if (!mLightSensorEnabled) {
- updateAutoBrightness = true;
- mLightSensorEnabled = true;
- mLightSensorEnableTime = SystemClock.uptimeMillis();
- mSensorManager.registerListener(mLightSensorListener, mLightSensor,
- LIGHT_SENSOR_RATE_MILLIS * 1000, mHandler);
- }
- } else {
- if (mLightSensorEnabled) {
- mLightSensorEnabled = false;
- mAmbientLuxValid = false;
- mRecentLightSamples = 0;
- mHandler.removeMessages(MSG_LIGHT_SENSOR_DEBOUNCED);
- mSensorManager.unregisterListener(mLightSensorListener);
- }
- }
- if (updateAutoBrightness) {
- updateAutoBrightness(false);
- }
- }
-
- private void handleLightSensorEvent(long time, float lux) {
- mHandler.removeMessages(MSG_LIGHT_SENSOR_DEBOUNCED);
-
- applyLightSensorMeasurement(time, lux);
- updateAmbientLux(time);
- }
-
- private void applyLightSensorMeasurement(long time, float lux) {
- // Update our filters.
- mRecentLightSamples += 1;
- if (mRecentLightSamples == 1) {
- mRecentShortTermAverageLux = lux;
- mRecentLongTermAverageLux = lux;
- } else {
- final long timeDelta = time - mLastObservedLuxTime;
- mRecentShortTermAverageLux += (lux - mRecentShortTermAverageLux)
- * timeDelta / (SHORT_TERM_AVERAGE_LIGHT_TIME_CONSTANT + timeDelta);
- mRecentLongTermAverageLux += (lux - mRecentLongTermAverageLux)
- * timeDelta / (LONG_TERM_AVERAGE_LIGHT_TIME_CONSTANT + timeDelta);
- }
-
- // Remember this sample value.
- mLastObservedLux = lux;
- mLastObservedLuxTime = time;
- }
-
- private void setAmbientLux(float lux) {
- mAmbientLux = lux;
- mBrighteningLuxThreshold = mAmbientLux * (1.0f + BRIGHTENING_LIGHT_HYSTERESIS);
- mDarkeningLuxThreshold = mAmbientLux * (1.0f - DARKENING_LIGHT_HYSTERESIS);
- }
-
- private void updateAmbientLux(long time) {
- // If the light sensor was just turned on then immediately update our initial
- // estimate of the current ambient light level.
- if (!mAmbientLuxValid) {
- final long timeWhenSensorWarmedUp =
- mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
- if (time < timeWhenSensorWarmedUp) {
- mHandler.sendEmptyMessageAtTime(MSG_LIGHT_SENSOR_DEBOUNCED,
- timeWhenSensorWarmedUp);
- return;
- }
- setAmbientLux(mRecentShortTermAverageLux);
- mAmbientLuxValid = true;
- mDebounceLuxDirection = 0;
- mDebounceLuxTime = time;
- if (DEBUG) {
- Slog.d(TAG, "updateAmbientLux: Initializing: "
- + ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux
- + ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux
- + ", mAmbientLux=" + mAmbientLux);
- }
- updateAutoBrightness(true);
- } else if (mRecentShortTermAverageLux > mBrighteningLuxThreshold
- && mRecentLongTermAverageLux > mBrighteningLuxThreshold) {
- // The ambient environment appears to be brightening.
- if (mDebounceLuxDirection <= 0) {
- mDebounceLuxDirection = 1;
- mDebounceLuxTime = time;
- if (DEBUG) {
- Slog.d(TAG, "updateAmbientLux: Possibly brightened, waiting for "
- + BRIGHTENING_LIGHT_DEBOUNCE + " ms: "
- + "mBrighteningLuxThreshold=" + mBrighteningLuxThreshold
- + ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux
- + ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux
- + ", mAmbientLux=" + mAmbientLux);
- }
- }
- long debounceTime = mDebounceLuxTime + BRIGHTENING_LIGHT_DEBOUNCE;
- if (time < debounceTime) {
- mHandler.sendEmptyMessageAtTime(MSG_LIGHT_SENSOR_DEBOUNCED, debounceTime);
- return;
- }
- setAmbientLux(mRecentShortTermAverageLux);
- if (DEBUG) {
- Slog.d(TAG, "updateAmbientLux: Brightened: "
- + "mBrighteningLuxThreshold=" + mBrighteningLuxThreshold
- + ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux
- + ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux
- + ", mAmbientLux=" + mAmbientLux);
- }
- updateAutoBrightness(true);
- } else if (mRecentShortTermAverageLux < mDarkeningLuxThreshold
- && mRecentLongTermAverageLux < mDarkeningLuxThreshold) {
- // The ambient environment appears to be darkening.
- if (mDebounceLuxDirection >= 0) {
- mDebounceLuxDirection = -1;
- mDebounceLuxTime = time;
- if (DEBUG) {
- Slog.d(TAG, "updateAmbientLux: Possibly darkened, waiting for "
- + DARKENING_LIGHT_DEBOUNCE + " ms: "
- + "mDarkeningLuxThreshold=" + mDarkeningLuxThreshold
- + ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux
- + ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux
- + ", mAmbientLux=" + mAmbientLux);
- }
- }
- long debounceTime = mDebounceLuxTime + DARKENING_LIGHT_DEBOUNCE;
- if (time < debounceTime) {
- mHandler.sendEmptyMessageAtTime(MSG_LIGHT_SENSOR_DEBOUNCED, debounceTime);
- return;
- }
- // Be conservative about reducing the brightness, only reduce it a little bit
- // at a time to avoid having to bump it up again soon.
- setAmbientLux(Math.max(mRecentShortTermAverageLux, mRecentLongTermAverageLux));
- if (DEBUG) {
- Slog.d(TAG, "updateAmbientLux: Darkened: "
- + "mDarkeningLuxThreshold=" + mDarkeningLuxThreshold
- + ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux
- + ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux
- + ", mAmbientLux=" + mAmbientLux);
- }
- updateAutoBrightness(true);
- } else if (mDebounceLuxDirection != 0) {
- // No change or change is within the hysteresis thresholds.
- mDebounceLuxDirection = 0;
- mDebounceLuxTime = time;
- if (DEBUG) {
- Slog.d(TAG, "updateAmbientLux: Canceled debounce: "
- + "mBrighteningLuxThreshold=" + mBrighteningLuxThreshold
- + ", mDarkeningLuxThreshold=" + mDarkeningLuxThreshold
- + ", mRecentShortTermAverageLux=" + mRecentShortTermAverageLux
- + ", mRecentLongTermAverageLux=" + mRecentLongTermAverageLux
- + ", mAmbientLux=" + mAmbientLux);
- }
- }
-
- // Now that we've done all of that, we haven't yet posted a debounce
- // message. So consider the case where current lux is beyond the
- // threshold. It's possible that the light sensor may not report values
- // if the light level does not change, so we need to occasionally
- // synthesize sensor readings in order to make sure the brightness is
- // adjusted accordingly. Note these thresholds may have changed since
- // we entered the function because we called setAmbientLux and
- // updateAutoBrightness along the way.
- if (mLastObservedLux > mBrighteningLuxThreshold
- || mLastObservedLux < mDarkeningLuxThreshold) {
- mHandler.sendEmptyMessageAtTime(MSG_LIGHT_SENSOR_DEBOUNCED,
- time + SYNTHETIC_LIGHT_SENSOR_RATE_MILLIS);
- }
- }
-
- private void debounceLightSensor() {
- if (mLightSensorEnabled) {
- long time = SystemClock.uptimeMillis();
- if (time >= mLastObservedLuxTime + SYNTHETIC_LIGHT_SENSOR_RATE_MILLIS) {
- if (DEBUG) {
- Slog.d(TAG, "debounceLightSensor: Synthesizing light sensor measurement "
- + "after " + (time - mLastObservedLuxTime) + " ms.");
- }
- applyLightSensorMeasurement(time, mLastObservedLux);
- }
- updateAmbientLux(time);
- }
- }
-
- private void updateAutoBrightness(boolean sendUpdate) {
- if (!mAmbientLuxValid) {
- return;
- }
-
- float value = mScreenAutoBrightnessSpline.interpolate(mAmbientLux);
- float gamma = 1.0f;
-
- if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT
- && mPowerRequest.screenAutoBrightnessAdjustment != 0.0f) {
- final float adjGamma = FloatMath.pow(SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA,
- Math.min(1.0f, Math.max(-1.0f,
- -mPowerRequest.screenAutoBrightnessAdjustment)));
- gamma *= adjGamma;
- if (DEBUG) {
- Slog.d(TAG, "updateAutoBrightness: adjGamma=" + adjGamma);
- }
- }
-
- if (USE_TWILIGHT_ADJUSTMENT) {
- TwilightState state = mTwilight.getCurrentState();
- if (state != null && state.isNight()) {
- final long now = System.currentTimeMillis();
- final float earlyGamma =
- getTwilightGamma(now, state.getYesterdaySunset(), state.getTodaySunrise());
- final float lateGamma =
- getTwilightGamma(now, state.getTodaySunset(), state.getTomorrowSunrise());
- gamma *= earlyGamma * lateGamma;
- if (DEBUG) {
- Slog.d(TAG, "updateAutoBrightness: earlyGamma=" + earlyGamma
- + ", lateGamma=" + lateGamma);
- }
- }
- }
-
- if (gamma != 1.0f) {
- final float in = value;
- value = FloatMath.pow(value, gamma);
- if (DEBUG) {
- Slog.d(TAG, "updateAutoBrightness: gamma=" + gamma
- + ", in=" + in + ", out=" + value);
- }
- }
-
- int newScreenAutoBrightness = clampScreenBrightness(
- Math.round(value * PowerManager.BRIGHTNESS_ON));
- if (mScreenAutoBrightness != newScreenAutoBrightness) {
- if (DEBUG) {
- Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness="
- + mScreenAutoBrightness + ", newScreenAutoBrightness="
- + newScreenAutoBrightness);
- }
-
- mScreenAutoBrightness = newScreenAutoBrightness;
- mLastScreenAutoBrightnessGamma = gamma;
- if (sendUpdate) {
- sendUpdatePowerState();
- }
- }
- }
-
- private static float getTwilightGamma(long now, long lastSunset, long nextSunrise) {
- if (lastSunset < 0 || nextSunrise < 0
- || now < lastSunset || now > nextSunrise) {
- return 1.0f;
- }
-
- if (now < lastSunset + TWILIGHT_ADJUSTMENT_TIME) {
- return lerp(1.0f, TWILIGHT_ADJUSTMENT_MAX_GAMMA,
- (float)(now - lastSunset) / TWILIGHT_ADJUSTMENT_TIME);
- }
-
- if (now > nextSunrise - TWILIGHT_ADJUSTMENT_TIME) {
- return lerp(1.0f, TWILIGHT_ADJUSTMENT_MAX_GAMMA,
- (float)(nextSunrise - now) / TWILIGHT_ADJUSTMENT_TIME);
- }
-
- return TWILIGHT_ADJUSTMENT_MAX_GAMMA;
- }
-
- private static float lerp(float x, float y, float alpha) {
- return x + (y - x) * alpha;
- }
-
private void sendOnStateChangedWithWakelock() {
mDisplaySuspendBlocker.acquire();
mCallbackHandler.post(mOnStateChangedRunnable);
@@ -1228,8 +821,6 @@ final class DisplayPowerController {
pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
pw.println(" mUseSoftwareAutoBrightnessConfig="
+ mUseSoftwareAutoBrightnessConfig);
- pw.println(" mScreenAutoBrightnessSpline=" + mScreenAutoBrightnessSpline);
- pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
mHandler.runWithScissors(new Runnable() {
@Override
@@ -1253,25 +844,7 @@ final class DisplayPowerController {
pw.println(" mPendingProximityDebounceTime="
+ TimeUtils.formatUptime(mPendingProximityDebounceTime));
pw.println(" mScreenOffBecauseOfProximity=" + mScreenOffBecauseOfProximity);
-
- pw.println(" mLightSensor=" + mLightSensor);
- pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
- pw.println(" mLightSensorEnableTime="
- + TimeUtils.formatUptime(mLightSensorEnableTime));
- pw.println(" mAmbientLux=" + mAmbientLux);
- pw.println(" mAmbientLuxValid=" + mAmbientLuxValid);
- pw.println(" mLastObservedLux=" + mLastObservedLux);
- pw.println(" mLastObservedLuxTime="
- + TimeUtils.formatUptime(mLastObservedLuxTime));
- pw.println(" mRecentLightSamples=" + mRecentLightSamples);
- pw.println(" mRecentShortTermAverageLux=" + mRecentShortTermAverageLux);
- pw.println(" mRecentLongTermAverageLux=" + mRecentLongTermAverageLux);
- pw.println(" mDebounceLuxDirection=" + mDebounceLuxDirection);
- pw.println(" mDebounceLuxTime=" + TimeUtils.formatUptime(mDebounceLuxTime));
- pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
pw.println(" mUsingScreenAutoBrightness=" + mUsingScreenAutoBrightness);
- pw.println(" mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma);
- pw.println(" mTwilight.getCurrentState()=" + mTwilight.getCurrentState());
if (mElectronBeamOnAnimator != null) {
pw.println(" mElectronBeamOnAnimator.isStarted()=" +
@@ -1285,6 +858,11 @@ final class DisplayPowerController {
if (mPowerState != null) {
mPowerState.dump(pw);
}
+
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.dump(pw);
+ }
+
}
private static String proximityToString(int state) {
@@ -1300,6 +878,39 @@ final class DisplayPowerController {
}
}
+ private static Spline createAutoBrightnessSpline(int[] lux, int[] brightness) {
+ try {
+ final int n = brightness.length;
+ float[] x = new float[n];
+ float[] y = new float[n];
+ y[0] = normalizeAbsoluteBrightness(brightness[0]);
+ for (int i = 1; i < n; i++) {
+ x[i] = lux[i - 1];
+ y[i] = normalizeAbsoluteBrightness(brightness[i]);
+ }
+
+ Spline spline = Spline.createMonotoneCubicSpline(x, y);
+ if (DEBUG) {
+ Slog.d(TAG, "Auto-brightness spline: " + spline);
+ for (float v = 1f; v < lux[lux.length - 1] * 1.25f; v *= 1.25f) {
+ Slog.d(TAG, String.format(" %7.1f: %7.1f", v, spline.interpolate(v)));
+ }
+ }
+ return spline;
+ } catch (IllegalArgumentException ex) {
+ Slog.e(TAG, "Could not create auto-brightness spline.", ex);
+ return null;
+ }
+ }
+
+ private static float normalizeAbsoluteBrightness(int value) {
+ return (float)clampAbsoluteBrightness(value) / PowerManager.BRIGHTNESS_ON;
+ }
+
+ private static int clampAbsoluteBrightness(int value) {
+ return MathUtils.constrain(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
+ }
+
/**
* Asynchronous callbacks from the power controller to the power manager service.
*/
@@ -1324,10 +935,6 @@ final class DisplayPowerController {
case MSG_PROXIMITY_SENSOR_DEBOUNCED:
debounceProximitySensor();
break;
-
- case MSG_LIGHT_SENSOR_DEBOUNCED:
- debounceLightSensor();
- break;
}
}
}
@@ -1348,28 +955,4 @@ final class DisplayPowerController {
// Not used.
}
};
-
- private final SensorEventListener mLightSensorListener = new SensorEventListener() {
- @Override
- public void onSensorChanged(SensorEvent event) {
- if (mLightSensorEnabled) {
- final long time = SystemClock.uptimeMillis();
- final float lux = event.values[0];
- handleLightSensorEvent(time, lux);
- }
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- // Not used.
- }
- };
-
- private final TwilightListener mTwilightListener = new TwilightListener() {
- @Override
- public void onTwilightStateChanged() {
- mTwilightChanged = true;
- updatePowerState();
- }
- };
}
diff --git a/services/core/java/com/android/server/power/DisplayPowerRequest.java b/services/core/java/com/android/server/power/DisplayPowerRequest.java
index 6061a40..c5e46cb 100644
--- a/services/core/java/com/android/server/power/DisplayPowerRequest.java
+++ b/services/core/java/com/android/server/power/DisplayPowerRequest.java
@@ -132,4 +132,13 @@ final class DisplayPowerRequest {
+ ", useAutoBrightness=" + useAutoBrightness
+ ", blockScreenOn=" + blockScreenOn;
}
+
+ public static boolean wantScreenOn(int state) {
+ switch(state) {
+ case SCREEN_STATE_DIM:
+ case SCREEN_STATE_BRIGHT:
+ return true;
+ }
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 264e2e9..e1ccf46 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -34,6 +34,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
+import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -129,7 +130,7 @@ final class Notifier {
* Called when a wake lock is acquired.
*/
public void onWakeLockAcquired(int flags, String tag, String packageName,
- int ownerUid, int ownerPid, WorkSource workSource) {
+ int ownerUid, int ownerPid, WorkSource workSource, String historyTag) {
if (DEBUG) {
Slog.d(TAG, "onWakeLockAcquired: flags=" + flags + ", tag=\"" + tag
+ "\", packageName=" + packageName
@@ -139,10 +140,14 @@ final class Notifier {
try {
final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
+ boolean unimportantForLogging = (flags&PowerManager.UNIMPORTANT_FOR_LOGGING) != 0
+ && ownerUid == Process.SYSTEM_UID;
if (workSource != null) {
- mBatteryStats.noteStartWakelockFromSource(workSource, ownerPid, tag, monitorType);
+ mBatteryStats.noteStartWakelockFromSource(workSource, ownerPid, tag, historyTag,
+ monitorType, unimportantForLogging);
} else {
- mBatteryStats.noteStartWakelock(ownerUid, ownerPid, tag, monitorType);
+ mBatteryStats.noteStartWakelock(ownerUid, ownerPid, tag, historyTag,
+ monitorType, unimportantForLogging);
// XXX need to deal with disabled operations.
mAppOps.startOperation(AppOpsManager.getToken(mAppOps),
AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index c31de05..e7bbf1c 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -632,7 +632,7 @@ public final class PowerManagerService extends com.android.server.SystemService
}
private void acquireWakeLockInternal(IBinder lock, int flags, String tag, String packageName,
- WorkSource ws, int uid, int pid) {
+ WorkSource ws, String historyTag, int uid, int pid) {
synchronized (mLock) {
if (DEBUG_SPEW) {
Slog.d(TAG, "acquireWakeLockInternal: lock=" + Objects.hashCode(lock)
@@ -647,11 +647,11 @@ public final class PowerManagerService extends com.android.server.SystemService
if (!wakeLock.hasSameProperties(flags, tag, ws, uid, pid)) {
// Update existing wake lock. This shouldn't happen but is harmless.
notifyWakeLockReleasedLocked(wakeLock);
- wakeLock.updateProperties(flags, tag, packageName, ws, uid, pid);
+ wakeLock.updateProperties(flags, tag, packageName, ws, historyTag, uid, pid);
notifyWakeLockAcquiredLocked(wakeLock);
}
} else {
- wakeLock = new WakeLock(lock, flags, tag, packageName, ws, uid, pid);
+ wakeLock = new WakeLock(lock, flags, tag, packageName, ws, historyTag, uid, pid);
try {
lock.linkToDeath(wakeLock, 0);
} catch (RemoteException ex) {
@@ -786,7 +786,8 @@ public final class PowerManagerService extends com.android.server.SystemService
if (mSystemReady) {
wakeLock.mNotifiedAcquired = true;
mNotifier.onWakeLockAcquired(wakeLock.mFlags, wakeLock.mTag, wakeLock.mPackageName,
- wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource);
+ wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource,
+ wakeLock.mHistoryTag);
}
}
@@ -1982,9 +1983,10 @@ public final class PowerManagerService extends com.android.server.SystemService
}
/**
- * Low-level function to reboot the device. On success, this function
- * doesn't return. If more than 5 seconds passes from the time,
- * a reboot is requested, this method returns.
+ * Low-level function to reboot the device. On success, this
+ * function doesn't return. If more than 20 seconds passes from
+ * the time a reboot is requested (120 seconds for reboot to
+ * recovery), this method returns.
*
* @param reason code to pass to the kernel (e.g. "recovery"), or null.
*/
@@ -1992,9 +1994,24 @@ public final class PowerManagerService extends com.android.server.SystemService
if (reason == null) {
reason = "";
}
- SystemProperties.set("sys.powerctl", "reboot," + reason);
+ long duration;
+ if (reason.equals(PowerManager.REBOOT_RECOVERY)) {
+ // If we are rebooting to go into recovery, instead of
+ // setting sys.powerctl directly we'll start the
+ // pre-recovery service which will do some preparation for
+ // recovery and then reboot for us.
+ //
+ // This preparation can take more than 20 seconds if
+ // there's a very large update package, so lengthen the
+ // timeout.
+ SystemProperties.set("ctl.start", "pre-recovery");
+ duration = 120 * 1000L;
+ } else {
+ SystemProperties.set("sys.powerctl", "reboot," + reason);
+ duration = 20 * 1000L;
+ }
try {
- Thread.sleep(20000);
+ Thread.sleep(duration);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
@@ -2259,17 +2276,19 @@ public final class PowerManagerService extends com.android.server.SystemService
public String mTag;
public final String mPackageName;
public WorkSource mWorkSource;
+ public String mHistoryTag;
public final int mOwnerUid;
public final int mOwnerPid;
public boolean mNotifiedAcquired;
public WakeLock(IBinder lock, int flags, String tag, String packageName,
- WorkSource workSource, int ownerUid, int ownerPid) {
+ WorkSource workSource, String historyTag, int ownerUid, int ownerPid) {
mLock = lock;
mFlags = flags;
mTag = tag;
mPackageName = packageName;
mWorkSource = copyWorkSource(workSource);
+ mHistoryTag = historyTag;
mOwnerUid = ownerUid;
mOwnerPid = ownerPid;
}
@@ -2289,7 +2308,7 @@ public final class PowerManagerService extends com.android.server.SystemService
}
public void updateProperties(int flags, String tag, String packageName,
- WorkSource workSource, int ownerUid, int ownerPid) {
+ WorkSource workSource, String historyTag, int ownerUid, int ownerPid) {
if (!mPackageName.equals(packageName)) {
throw new IllegalStateException("Existing wake lock package name changed: "
+ mPackageName + " to " + packageName);
@@ -2305,6 +2324,7 @@ public final class PowerManagerService extends com.android.server.SystemService
mFlags = flags;
mTag = tag;
updateWorkSource(workSource);
+ mHistoryTag = historyTag;
}
public boolean hasSameWorkSource(WorkSource workSource) {
@@ -2502,12 +2522,12 @@ public final class PowerManagerService extends com.android.server.SystemService
@Override // Binder call
public void acquireWakeLockWithUid(IBinder lock, int flags, String tag,
String packageName, int uid) {
- acquireWakeLock(lock, flags, tag, packageName, new WorkSource(uid));
+ acquireWakeLock(lock, flags, tag, packageName, new WorkSource(uid), null);
}
@Override // Binder call
public void acquireWakeLock(IBinder lock, int flags, String tag, String packageName,
- WorkSource ws) {
+ WorkSource ws, String historyTag) {
if (lock == null) {
throw new IllegalArgumentException("lock must not be null");
}
@@ -2528,7 +2548,7 @@ public final class PowerManagerService extends com.android.server.SystemService
final int pid = Binder.getCallingPid();
final long ident = Binder.clearCallingIdentity();
try {
- acquireWakeLockInternal(lock, flags, tag, packageName, ws, uid, pid);
+ acquireWakeLockInternal(lock, flags, tag, packageName, ws, historyTag, uid, pid);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2702,6 +2722,9 @@ public final class PowerManagerService extends com.android.server.SystemService
@Override // Binder call
public void reboot(boolean confirm, String reason, boolean wait) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
+ if (PowerManager.REBOOT_RECOVERY.equals(reason)) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
+ }
final long ident = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/search/SearchManagerService.java b/services/core/java/com/android/server/search/SearchManagerService.java
index b5d81d1..486477a 100644
--- a/services/core/java/com/android/server/search/SearchManagerService.java
+++ b/services/core/java/com/android/server/search/SearchManagerService.java
@@ -17,7 +17,6 @@
package com.android.server.search;
import android.app.ActivityManager;
-import android.app.ActivityManagerNative;
import android.app.AppGlobals;
import android.app.ISearchManager;
import android.app.SearchManager;
@@ -39,7 +38,6 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.util.Log;
-import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.content.PackageMonitor;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 2ae467e..8219eb5 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -23,9 +23,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.util.Slog;
@@ -207,6 +205,10 @@ public class StatusBarManagerService extends IStatusBarService.Stub
@Override
public void disable(int what, IBinder token, String pkg) {
+ if (!mNotificationDelegate.allowDisable(what, token, pkg)) {
+ if (SPEW) Slog.d(TAG, "Blocking disable request from " + pkg);
+ return;
+ }
disableInternal(mCurrentUserId, what, token, pkg);
}
@@ -676,26 +678,4 @@ public class StatusBarManagerService extends IStatusBarService.Stub
}
}
}
-
- private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
- || Intent.ACTION_SCREEN_OFF.equals(action)) {
- collapsePanels();
- }
- /*
- else if (Telephony.Intents.SPN_STRINGS_UPDATED_ACTION.equals(action)) {
- updateNetworkName(intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_SPN, false),
- intent.getStringExtra(Telephony.Intents.EXTRA_SPN),
- intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_PLMN, false),
- intent.getStringExtra(Telephony.Intents.EXTRA_PLMN));
- }
- else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
- updateResources();
- }
- */
- }
- };
-
}
diff --git a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
index 43a99e0..260e97a 100644
--- a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
+++ b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
@@ -454,6 +454,7 @@ public class DeviceStorageMonitorService extends SystemService {
notification.tickerText = title;
notification.flags |= Notification.FLAG_NO_CLEAR;
notification.setLatestEventInfo(context, title, details, intent);
+ notification.visibility = Notification.VISIBILITY_PUBLIC;
mNotificationMgr.notifyAsUser(null, LOW_MEMORY_NOTIFICATION_ID, notification,
UserHandle.ALL);
context.sendStickyBroadcastAsUser(mStorageLowIntent, UserHandle.ALL);
diff --git a/services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java b/services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
index 9601e9a..1a68fb3 100644
--- a/services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
@@ -21,7 +21,6 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.provider.Settings;
-import android.os.FileUtils;
import android.util.Base64;
import android.util.EventLog;
import android.util.Slog;
@@ -30,18 +29,15 @@ import com.android.server.EventLogTags;
import java.io.ByteArrayInputStream;
import java.io.File;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.IOException;
-import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
-import java.security.SignatureException;
import libcore.io.IoUtils;
diff --git a/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java b/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
index e430814..fdc604f 100644
--- a/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
@@ -18,8 +18,6 @@ package com.android.server.updates;
import android.content.Context;
import android.content.Intent;
-import android.os.FileUtils;
-import android.os.SELinux;
import android.os.SystemProperties;
import android.provider.Settings;
import android.util.Base64;
diff --git a/services/core/java/com/android/server/updates/TZInfoInstallReceiver.java b/services/core/java/com/android/server/updates/TZInfoInstallReceiver.java
index 83adbdb..2fe68f8 100644
--- a/services/core/java/com/android/server/updates/TZInfoInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/TZInfoInstallReceiver.java
@@ -17,7 +17,6 @@
package com.android.server.updates;
import android.util.Base64;
-import android.util.Slog;
import java.io.IOException;
diff --git a/services/core/java/com/android/server/wifi/README.txt b/services/core/java/com/android/server/wifi/README.txt
deleted file mode 100644
index 39e1475..0000000
--- a/services/core/java/com/android/server/wifi/README.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-WifiService: Implements the IWifiManager 3rd party API. The API and the device state information (screen on/off, battery state, sleep policy) go as input into the WifiController which tracks high level states as to whether STA or AP mode is operational and controls the WifiStateMachine to handle bringup and shut down.
-
-WifiController: Acts as a controller to the WifiStateMachine based on various inputs (API and device state). Runs on the same thread created in WifiService.
-
-WifiSettingsStore: Tracks the various settings (wifi toggle, airplane toggle, tethering toggle, scan mode toggle) and provides API to figure if wifi should be turned on or off.
-
-WifiTrafficPoller: Polls traffic on wifi and notifies apps listening on it.
-
-WifiNotificationController: Controls whether the open network notification is displayed or not based on the scan results.
-
-WifiStateMachine: Tracks the various states on STA and AP connectivity and handles bring up and shut down.
-
-
-Feature description:
-
-Scan-only mode with Wi-Fi turned off:
- - Setup wizard opts user into allowing scanning for improved location. We show no further dialogs in setup wizard since the user has just opted into the feature. This is the reason WifiService listens to DEVICE_PROVISIONED setting.
- - Once the user has his device provisioned, turning off Wi-Fi from settings or from a third party app will show up a dialog reminding the user that scan mode will be on even though Wi-Fi is being turned off. The user has the choice to turn this notification off.
- - In the scan mode, the device continues to allow scanning from any app with Wi-Fi turned off. This is done by disabling all networks and allowing only scans to be passed.
diff --git a/services/core/java/com/android/server/wifi/WifiController.java b/services/core/java/com/android/server/wifi/WifiController.java
deleted file mode 100644
index a3d514e..0000000
--- a/services/core/java/com/android/server/wifi/WifiController.java
+++ /dev/null
@@ -1,751 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wifi;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.ContentObserver;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiManager;
-import static android.net.wifi.WifiManager.WIFI_MODE_FULL;
-import static android.net.wifi.WifiManager.WIFI_MODE_FULL_HIGH_PERF;
-import static android.net.wifi.WifiManager.WIFI_MODE_SCAN_ONLY;
-import android.net.wifi.WifiStateMachine;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.SystemClock;
-import android.os.WorkSource;
-import android.provider.Settings;
-import android.util.Slog;
-
-import com.android.internal.util.Protocol;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
-import com.android.server.wifi.WifiService.LockList;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-class WifiController extends StateMachine {
- private static final String TAG = "WifiController";
- private static final boolean DBG = false;
- private Context mContext;
- private boolean mScreenOff;
- private boolean mDeviceIdle;
- private int mPluggedType;
- private int mStayAwakeConditions;
- private long mIdleMillis;
- private int mSleepPolicy;
- private boolean mFirstUserSignOnSeen = false;
-
- private AlarmManager mAlarmManager;
- private PendingIntent mIdleIntent;
- private static final int IDLE_REQUEST = 0;
-
- /**
- * See {@link Settings.Global#WIFI_IDLE_MS}. This is the default value if a
- * Settings.Global value is not present. This timeout value is chosen as
- * the approximate point at which the battery drain caused by Wi-Fi
- * being enabled but not active exceeds the battery drain caused by
- * re-establishing a connection to the mobile data network.
- */
- private static final long DEFAULT_IDLE_MS = 15 * 60 * 1000; /* 15 minutes */
-
- /**
- * See {@link Settings.Global#WIFI_REENABLE_DELAY_MS}. This is the default value if a
- * Settings.Global value is not present. This is the minimum time after wifi is disabled
- * we'll act on an enable. Enable requests received before this delay will be deferred.
- */
- private static final long DEFAULT_REENABLE_DELAY_MS = 500;
-
- // finding that delayed messages can sometimes be delivered earlier than expected
- // probably rounding errors.. add a margin to prevent problems
- private static final long DEFER_MARGIN_MS = 5;
-
- NetworkInfo mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
-
- private static final String ACTION_DEVICE_IDLE =
- "com.android.server.WifiManager.action.DEVICE_IDLE";
-
- /* References to values tracked in WifiService */
- final WifiStateMachine mWifiStateMachine;
- final WifiSettingsStore mSettingsStore;
- final LockList mLocks;
-
- /**
- * Temporary for computing UIDS that are responsible for starting WIFI.
- * Protected by mWifiStateTracker lock.
- */
- private final WorkSource mTmpWorkSource = new WorkSource();
-
- private long mReEnableDelayMillis;
-
- private static final int BASE = Protocol.BASE_WIFI_CONTROLLER;
-
- static final int CMD_EMERGENCY_MODE_CHANGED = BASE + 1;
- static final int CMD_SCREEN_ON = BASE + 2;
- static final int CMD_SCREEN_OFF = BASE + 3;
- static final int CMD_BATTERY_CHANGED = BASE + 4;
- static final int CMD_DEVICE_IDLE = BASE + 5;
- static final int CMD_LOCKS_CHANGED = BASE + 6;
- static final int CMD_SCAN_ALWAYS_MODE_CHANGED = BASE + 7;
- static final int CMD_WIFI_TOGGLED = BASE + 8;
- static final int CMD_AIRPLANE_TOGGLED = BASE + 9;
- static final int CMD_SET_AP = BASE + 10;
- static final int CMD_DEFERRED_TOGGLE = BASE + 11;
- static final int CMD_USER_PRESENT = BASE + 12;
-
- private DefaultState mDefaultState = new DefaultState();
- private StaEnabledState mStaEnabledState = new StaEnabledState();
- private ApStaDisabledState mApStaDisabledState = new ApStaDisabledState();
- private StaDisabledWithScanState mStaDisabledWithScanState = new StaDisabledWithScanState();
- private ApEnabledState mApEnabledState = new ApEnabledState();
- private DeviceActiveState mDeviceActiveState = new DeviceActiveState();
- private DeviceInactiveState mDeviceInactiveState = new DeviceInactiveState();
- private ScanOnlyLockHeldState mScanOnlyLockHeldState = new ScanOnlyLockHeldState();
- private FullLockHeldState mFullLockHeldState = new FullLockHeldState();
- private FullHighPerfLockHeldState mFullHighPerfLockHeldState = new FullHighPerfLockHeldState();
- private NoLockHeldState mNoLockHeldState = new NoLockHeldState();
- private EcmState mEcmState = new EcmState();
-
- WifiController(Context context, WifiService service, Looper looper) {
- super(TAG, looper);
- mContext = context;
- mWifiStateMachine = service.mWifiStateMachine;
- mSettingsStore = service.mSettingsStore;
- mLocks = service.mLocks;
-
- mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
- Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null);
- mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0);
-
- addState(mDefaultState);
- addState(mApStaDisabledState, mDefaultState);
- addState(mStaEnabledState, mDefaultState);
- addState(mDeviceActiveState, mStaEnabledState);
- addState(mDeviceInactiveState, mStaEnabledState);
- addState(mScanOnlyLockHeldState, mDeviceInactiveState);
- addState(mFullLockHeldState, mDeviceInactiveState);
- addState(mFullHighPerfLockHeldState, mDeviceInactiveState);
- addState(mNoLockHeldState, mDeviceInactiveState);
- addState(mStaDisabledWithScanState, mDefaultState);
- addState(mApEnabledState, mDefaultState);
- addState(mEcmState, mDefaultState);
- if (mSettingsStore.isScanAlwaysAvailable()) {
- setInitialState(mStaDisabledWithScanState);
- } else {
- setInitialState(mApStaDisabledState);
- }
- setLogRecSize(100);
- setLogOnlyTransitions(false);
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(ACTION_DEVICE_IDLE);
- filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
- mContext.registerReceiver(
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(ACTION_DEVICE_IDLE)) {
- sendMessage(CMD_DEVICE_IDLE);
- } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
- mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
- WifiManager.EXTRA_NETWORK_INFO);
- }
- }
- },
- new IntentFilter(filter));
-
- initializeAndRegisterForSettingsChange(looper);
- }
-
- private void initializeAndRegisterForSettingsChange(Looper looper) {
- Handler handler = new Handler(looper);
- readStayAwakeConditions();
- registerForStayAwakeModeChange(handler);
- readWifiIdleTime();
- registerForWifiIdleTimeChange(handler);
- readWifiSleepPolicy();
- registerForWifiSleepPolicyChange(handler);
- readWifiReEnableDelay();
- }
-
- private void readStayAwakeConditions() {
- mStayAwakeConditions = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
- }
-
- private void readWifiIdleTime() {
- mIdleMillis = Settings.Global.getLong(mContext.getContentResolver(),
- Settings.Global.WIFI_IDLE_MS, DEFAULT_IDLE_MS);
- }
-
- private void readWifiSleepPolicy() {
- mSleepPolicy = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.WIFI_SLEEP_POLICY,
- Settings.Global.WIFI_SLEEP_POLICY_NEVER);
- }
-
- private void readWifiReEnableDelay() {
- mReEnableDelayMillis = Settings.Global.getLong(mContext.getContentResolver(),
- Settings.Global.WIFI_REENABLE_DELAY_MS, DEFAULT_REENABLE_DELAY_MS);
- }
-
- /**
- * Observes settings changes to scan always mode.
- */
- private void registerForStayAwakeModeChange(Handler handler) {
- ContentObserver contentObserver = new ContentObserver(handler) {
- @Override
- public void onChange(boolean selfChange) {
- readStayAwakeConditions();
- }
- };
-
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.STAY_ON_WHILE_PLUGGED_IN),
- false, contentObserver);
- }
-
- /**
- * Observes settings changes to scan always mode.
- */
- private void registerForWifiIdleTimeChange(Handler handler) {
- ContentObserver contentObserver = new ContentObserver(handler) {
- @Override
- public void onChange(boolean selfChange) {
- readWifiIdleTime();
- }
- };
-
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.WIFI_IDLE_MS),
- false, contentObserver);
- }
-
- /**
- * Observes changes to wifi sleep policy
- */
- private void registerForWifiSleepPolicyChange(Handler handler) {
- ContentObserver contentObserver = new ContentObserver(handler) {
- @Override
- public void onChange(boolean selfChange) {
- readWifiSleepPolicy();
- }
- };
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.WIFI_SLEEP_POLICY),
- false, contentObserver);
- }
-
- /**
- * Determines whether the Wi-Fi chipset should stay awake or be put to
- * sleep. Looks at the setting for the sleep policy and the current
- * conditions.
- *
- * @see #shouldDeviceStayAwake(int)
- */
- private boolean shouldWifiStayAwake(int pluggedType) {
- if (mSleepPolicy == Settings.Global.WIFI_SLEEP_POLICY_NEVER) {
- // Never sleep
- return true;
- } else if ((mSleepPolicy == Settings.Global.WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED) &&
- (pluggedType != 0)) {
- // Never sleep while plugged, and we're plugged
- return true;
- } else {
- // Default
- return shouldDeviceStayAwake(pluggedType);
- }
- }
-
- /**
- * Determine whether the bit value corresponding to {@code pluggedType} is set in
- * the bit string mStayAwakeConditions. This determines whether the device should
- * stay awake based on the current plugged type.
- *
- * @param pluggedType the type of plug (USB, AC, or none) for which the check is
- * being made
- * @return {@code true} if {@code pluggedType} indicates that the device is
- * supposed to stay awake, {@code false} otherwise.
- */
- private boolean shouldDeviceStayAwake(int pluggedType) {
- return (mStayAwakeConditions & pluggedType) != 0;
- }
-
- private void updateBatteryWorkSource() {
- mTmpWorkSource.clear();
- if (mDeviceIdle) {
- mLocks.updateWorkSource(mTmpWorkSource);
- }
- mWifiStateMachine.updateBatteryWorkSource(mTmpWorkSource);
- }
-
- class DefaultState extends State {
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_SCREEN_ON:
- mAlarmManager.cancel(mIdleIntent);
- mScreenOff = false;
- mDeviceIdle = false;
- updateBatteryWorkSource();
- break;
- case CMD_SCREEN_OFF:
- mScreenOff = true;
- /*
- * Set a timer to put Wi-Fi to sleep, but only if the screen is off
- * AND the "stay on while plugged in" setting doesn't match the
- * current power conditions (i.e, not plugged in, plugged in to USB,
- * or plugged in to AC).
- */
- if (!shouldWifiStayAwake(mPluggedType)) {
- //Delayed shutdown if wifi is connected
- if (mNetworkInfo.getDetailedState() ==
- NetworkInfo.DetailedState.CONNECTED) {
- if (DBG) Slog.d(TAG, "set idle timer: " + mIdleMillis + " ms");
- mAlarmManager.set(AlarmManager.RTC_WAKEUP,
- System.currentTimeMillis() + mIdleMillis, mIdleIntent);
- } else {
- sendMessage(CMD_DEVICE_IDLE);
- }
- }
- break;
- case CMD_DEVICE_IDLE:
- mDeviceIdle = true;
- updateBatteryWorkSource();
- break;
- case CMD_BATTERY_CHANGED:
- /*
- * Set a timer to put Wi-Fi to sleep, but only if the screen is off
- * AND we are transitioning from a state in which the device was supposed
- * to stay awake to a state in which it is not supposed to stay awake.
- * If "stay awake" state is not changing, we do nothing, to avoid resetting
- * the already-set timer.
- */
- int pluggedType = msg.arg1;
- if (DBG) Slog.d(TAG, "battery changed pluggedType: " + pluggedType);
- if (mScreenOff && shouldWifiStayAwake(mPluggedType) &&
- !shouldWifiStayAwake(pluggedType)) {
- long triggerTime = System.currentTimeMillis() + mIdleMillis;
- if (DBG) Slog.d(TAG, "set idle timer for " + mIdleMillis + "ms");
- mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, mIdleIntent);
- }
-
- mPluggedType = pluggedType;
- break;
- case CMD_SET_AP:
- case CMD_SCAN_ALWAYS_MODE_CHANGED:
- case CMD_LOCKS_CHANGED:
- case CMD_WIFI_TOGGLED:
- case CMD_AIRPLANE_TOGGLED:
- case CMD_EMERGENCY_MODE_CHANGED:
- break;
- case CMD_USER_PRESENT:
- mFirstUserSignOnSeen = true;
- break;
- case CMD_DEFERRED_TOGGLE:
- log("DEFERRED_TOGGLE ignored due to state change");
- break;
- default:
- throw new RuntimeException("WifiController.handleMessage " + msg.what);
- }
- return HANDLED;
- }
-
- }
-
- class ApStaDisabledState extends State {
- private int mDeferredEnableSerialNumber = 0;
- private boolean mHaveDeferredEnable = false;
- private long mDisabledTimestamp;
-
- @Override
- public void enter() {
- mWifiStateMachine.setSupplicantRunning(false);
- // Supplicant can't restart right away, so not the time we switched off
- mDisabledTimestamp = SystemClock.elapsedRealtime();
- mDeferredEnableSerialNumber++;
- mHaveDeferredEnable = false;
- }
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_WIFI_TOGGLED:
- case CMD_AIRPLANE_TOGGLED:
- if (mSettingsStore.isWifiToggleEnabled()) {
- if (doDeferEnable(msg)) {
- if (mHaveDeferredEnable) {
- // have 2 toggles now, inc serial number an ignore both
- mDeferredEnableSerialNumber++;
- }
- mHaveDeferredEnable = !mHaveDeferredEnable;
- break;
- }
- if (mDeviceIdle == false) {
- transitionTo(mDeviceActiveState);
- } else {
- checkLocksAndTransitionWhenDeviceIdle();
- }
- }
- break;
- case CMD_SCAN_ALWAYS_MODE_CHANGED:
- if (mSettingsStore.isScanAlwaysAvailable()) {
- transitionTo(mStaDisabledWithScanState);
- }
- break;
- case CMD_SET_AP:
- if (msg.arg1 == 1) {
- mWifiStateMachine.setHostApRunning((WifiConfiguration) msg.obj,
- true);
- transitionTo(mApEnabledState);
- }
- break;
- case CMD_DEFERRED_TOGGLE:
- if (msg.arg1 != mDeferredEnableSerialNumber) {
- log("DEFERRED_TOGGLE ignored due to serial mismatch");
- break;
- }
- log("DEFERRED_TOGGLE handled");
- sendMessage((Message)(msg.obj));
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
-
- private boolean doDeferEnable(Message msg) {
- long delaySoFar = SystemClock.elapsedRealtime() - mDisabledTimestamp;
- if (delaySoFar >= mReEnableDelayMillis) {
- return false;
- }
-
- log("WifiController msg " + msg + " deferred for " +
- (mReEnableDelayMillis - delaySoFar) + "ms");
-
- // need to defer this action.
- Message deferredMsg = obtainMessage(CMD_DEFERRED_TOGGLE);
- deferredMsg.obj = Message.obtain(msg);
- deferredMsg.arg1 = ++mDeferredEnableSerialNumber;
- sendMessageDelayed(deferredMsg, mReEnableDelayMillis - delaySoFar + DEFER_MARGIN_MS);
- return true;
- }
-
- }
-
- class StaEnabledState extends State {
- @Override
- public void enter() {
- mWifiStateMachine.setSupplicantRunning(true);
- }
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_WIFI_TOGGLED:
- if (! mSettingsStore.isWifiToggleEnabled()) {
- if (mSettingsStore.isScanAlwaysAvailable()) {
- transitionTo(mStaDisabledWithScanState);
- } else {
- transitionTo(mApStaDisabledState);
- }
- }
- break;
- case CMD_AIRPLANE_TOGGLED:
- /* When wi-fi is turned off due to airplane,
- * disable entirely (including scan)
- */
- if (! mSettingsStore.isWifiToggleEnabled()) {
- transitionTo(mApStaDisabledState);
- }
- break;
- case CMD_EMERGENCY_MODE_CHANGED:
- if (msg.arg1 == 1) {
- transitionTo(mEcmState);
- break;
- }
- default:
- return NOT_HANDLED;
-
- }
- return HANDLED;
- }
- }
-
- class StaDisabledWithScanState extends State {
- private int mDeferredEnableSerialNumber = 0;
- private boolean mHaveDeferredEnable = false;
- private long mDisabledTimestamp;
-
- @Override
- public void enter() {
- mWifiStateMachine.setSupplicantRunning(true);
- mWifiStateMachine.setOperationalMode(WifiStateMachine.SCAN_ONLY_WITH_WIFI_OFF_MODE);
- mWifiStateMachine.setDriverStart(true);
- // Supplicant can't restart right away, so not the time we switched off
- mDisabledTimestamp = SystemClock.elapsedRealtime();
- mDeferredEnableSerialNumber++;
- mHaveDeferredEnable = false;
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_WIFI_TOGGLED:
- if (mSettingsStore.isWifiToggleEnabled()) {
- if (doDeferEnable(msg)) {
- if (mHaveDeferredEnable) {
- // have 2 toggles now, inc serial number and ignore both
- mDeferredEnableSerialNumber++;
- }
- mHaveDeferredEnable = !mHaveDeferredEnable;
- break;
- }
- if (mDeviceIdle == false) {
- transitionTo(mDeviceActiveState);
- } else {
- checkLocksAndTransitionWhenDeviceIdle();
- }
- }
- break;
- case CMD_AIRPLANE_TOGGLED:
- if (mSettingsStore.isAirplaneModeOn() &&
- ! mSettingsStore.isWifiToggleEnabled()) {
- transitionTo(mApStaDisabledState);
- }
- case CMD_SCAN_ALWAYS_MODE_CHANGED:
- if (! mSettingsStore.isScanAlwaysAvailable()) {
- transitionTo(mApStaDisabledState);
- }
- break;
- case CMD_SET_AP:
- // Before starting tethering, turn off supplicant for scan mode
- if (msg.arg1 == 1) {
- deferMessage(msg);
- transitionTo(mApStaDisabledState);
- }
- break;
- case CMD_DEFERRED_TOGGLE:
- if (msg.arg1 != mDeferredEnableSerialNumber) {
- log("DEFERRED_TOGGLE ignored due to serial mismatch");
- break;
- }
- logd("DEFERRED_TOGGLE handled");
- sendMessage((Message)(msg.obj));
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
-
- private boolean doDeferEnable(Message msg) {
- long delaySoFar = SystemClock.elapsedRealtime() - mDisabledTimestamp;
- if (delaySoFar >= mReEnableDelayMillis) {
- return false;
- }
-
- log("WifiController msg " + msg + " deferred for " +
- (mReEnableDelayMillis - delaySoFar) + "ms");
-
- // need to defer this action.
- Message deferredMsg = obtainMessage(CMD_DEFERRED_TOGGLE);
- deferredMsg.obj = Message.obtain(msg);
- deferredMsg.arg1 = ++mDeferredEnableSerialNumber;
- sendMessageDelayed(deferredMsg, mReEnableDelayMillis - delaySoFar + DEFER_MARGIN_MS);
- return true;
- }
-
- }
-
- class ApEnabledState extends State {
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_AIRPLANE_TOGGLED:
- if (mSettingsStore.isAirplaneModeOn()) {
- mWifiStateMachine.setHostApRunning(null, false);
- transitionTo(mApStaDisabledState);
- }
- break;
- case CMD_SET_AP:
- if (msg.arg1 == 0) {
- mWifiStateMachine.setHostApRunning(null, false);
- transitionTo(mApStaDisabledState);
- }
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- class EcmState extends State {
- @Override
- public void enter() {
- mWifiStateMachine.setSupplicantRunning(false);
- }
-
- @Override
- public boolean processMessage(Message msg) {
- if (msg.what == CMD_EMERGENCY_MODE_CHANGED && msg.arg1 == 0) {
- if (mSettingsStore.isWifiToggleEnabled()) {
- if (mDeviceIdle == false) {
- transitionTo(mDeviceActiveState);
- } else {
- checkLocksAndTransitionWhenDeviceIdle();
- }
- } else if (mSettingsStore.isScanAlwaysAvailable()) {
- transitionTo(mStaDisabledWithScanState);
- } else {
- transitionTo(mApStaDisabledState);
- }
- return HANDLED;
- } else {
- return NOT_HANDLED;
- }
- }
- }
-
- /* Parent: StaEnabledState */
- class DeviceActiveState extends State {
- @Override
- public void enter() {
- mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
- mWifiStateMachine.setDriverStart(true);
- mWifiStateMachine.setHighPerfModeEnabled(false);
- }
-
- @Override
- public boolean processMessage(Message msg) {
- if (msg.what == CMD_DEVICE_IDLE) {
- checkLocksAndTransitionWhenDeviceIdle();
- // We let default state handle the rest of work
- } else if (msg.what == CMD_USER_PRESENT) {
- // TLS networks can't connect until user unlocks keystore. KeyStore
- // unlocks when the user punches PIN after the reboot. So use this
- // trigger to get those networks connected.
- if (mFirstUserSignOnSeen == false) {
- mWifiStateMachine.reloadTlsNetworksAndReconnect();
- }
- mFirstUserSignOnSeen = true;
- return HANDLED;
- }
- return NOT_HANDLED;
- }
- }
-
- /* Parent: StaEnabledState */
- class DeviceInactiveState extends State {
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_LOCKS_CHANGED:
- checkLocksAndTransitionWhenDeviceIdle();
- updateBatteryWorkSource();
- return HANDLED;
- case CMD_SCREEN_ON:
- transitionTo(mDeviceActiveState);
- // More work in default state
- return NOT_HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
- }
-
- /* Parent: DeviceInactiveState. Device is inactive, but an app is holding a scan only lock. */
- class ScanOnlyLockHeldState extends State {
- @Override
- public void enter() {
- mWifiStateMachine.setOperationalMode(WifiStateMachine.SCAN_ONLY_MODE);
- mWifiStateMachine.setDriverStart(true);
- }
- }
-
- /* Parent: DeviceInactiveState. Device is inactive, but an app is holding a full lock. */
- class FullLockHeldState extends State {
- @Override
- public void enter() {
- mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
- mWifiStateMachine.setDriverStart(true);
- mWifiStateMachine.setHighPerfModeEnabled(false);
- }
- }
-
- /* Parent: DeviceInactiveState. Device is inactive, but an app is holding a high perf lock. */
- class FullHighPerfLockHeldState extends State {
- @Override
- public void enter() {
- mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
- mWifiStateMachine.setDriverStart(true);
- mWifiStateMachine.setHighPerfModeEnabled(true);
- }
- }
-
- /* Parent: DeviceInactiveState. Device is inactive and no app is holding a wifi lock. */
- class NoLockHeldState extends State {
- @Override
- public void enter() {
- mWifiStateMachine.setDriverStart(false);
- }
- }
-
- private void checkLocksAndTransitionWhenDeviceIdle() {
- if (mLocks.hasLocks()) {
- switch (mLocks.getStrongestLockMode()) {
- case WIFI_MODE_FULL:
- transitionTo(mFullLockHeldState);
- break;
- case WIFI_MODE_FULL_HIGH_PERF:
- transitionTo(mFullHighPerfLockHeldState);
- break;
- case WIFI_MODE_SCAN_ONLY:
- transitionTo(mScanOnlyLockHeldState);
- break;
- default:
- loge("Illegal lock " + mLocks.getStrongestLockMode());
- }
- } else {
- if (mSettingsStore.isScanAlwaysAvailable()) {
- transitionTo(mScanOnlyLockHeldState);
- } else {
- transitionTo(mNoLockHeldState);
- }
- }
- }
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- super.dump(fd, pw, args);
-
- pw.println("mScreenOff " + mScreenOff);
- pw.println("mDeviceIdle " + mDeviceIdle);
- pw.println("mPluggedType " + mPluggedType);
- pw.println("mIdleMillis " + mIdleMillis);
- pw.println("mSleepPolicy " + mSleepPolicy);
- }
-}
diff --git a/services/core/java/com/android/server/wifi/WifiNotificationController.java b/services/core/java/com/android/server/wifi/WifiNotificationController.java
deleted file mode 100644
index a9206e0..0000000
--- a/services/core/java/com/android/server/wifi/WifiNotificationController.java
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wifi;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.TaskStackBuilder;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.ContentObserver;
-import android.net.NetworkInfo;
-import android.net.wifi.ScanResult;
-import android.net.wifi.WifiManager;
-import android.net.wifi.WifiStateMachine;
-import android.os.Handler;
-import android.os.Message;
-import android.os.UserHandle;
-import android.provider.Settings;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.List;
-
-/* Takes care of handling the "open wi-fi network available" notification @hide */
-final class WifiNotificationController {
- /**
- * The icon to show in the 'available networks' notification. This will also
- * be the ID of the Notification given to the NotificationManager.
- */
- private static final int ICON_NETWORKS_AVAILABLE =
- com.android.internal.R.drawable.stat_notify_wifi_in_range;
- /**
- * When a notification is shown, we wait this amount before possibly showing it again.
- */
- private final long NOTIFICATION_REPEAT_DELAY_MS;
- /**
- * Whether the user has set the setting to show the 'available networks' notification.
- */
- private boolean mNotificationEnabled;
- /**
- * Observes the user setting to keep {@link #mNotificationEnabled} in sync.
- */
- private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
- /**
- * The {@link System#currentTimeMillis()} must be at least this value for us
- * to show the notification again.
- */
- private long mNotificationRepeatTime;
- /**
- * The Notification object given to the NotificationManager.
- */
- private Notification mNotification;
- /**
- * Whether the notification is being shown, as set by us. That is, if the
- * user cancels the notification, we will not receive the callback so this
- * will still be true. We only guarantee if this is false, then the
- * notification is not showing.
- */
- private boolean mNotificationShown;
- /**
- * The number of continuous scans that must occur before consider the
- * supplicant in a scanning state. This allows supplicant to associate with
- * remembered networks that are in the scan results.
- */
- private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
- /**
- * The number of scans since the last network state change. When this
- * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the
- * supplicant to actually be scanning. When the network state changes to
- * something other than scanning, we reset this to 0.
- */
- private int mNumScansSinceNetworkStateChange;
-
- private final Context mContext;
- private final WifiStateMachine mWifiStateMachine;
- private NetworkInfo mNetworkInfo;
- private volatile int mWifiState;
-
- WifiNotificationController(Context context, WifiStateMachine wsm) {
- mContext = context;
- mWifiStateMachine = wsm;
- mWifiState = WifiManager.WIFI_STATE_UNKNOWN;
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
- filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
- filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
-
- mContext.registerReceiver(
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
- mWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
- WifiManager.WIFI_STATE_UNKNOWN);
- resetNotification();
- } else if (intent.getAction().equals(
- WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
- mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
- WifiManager.EXTRA_NETWORK_INFO);
- // reset & clear notification on a network connect & disconnect
- switch(mNetworkInfo.getDetailedState()) {
- case CONNECTED:
- case DISCONNECTED:
- case CAPTIVE_PORTAL_CHECK:
- resetNotification();
- break;
- }
- } else if (intent.getAction().equals(
- WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
- checkAndSetNotification(mNetworkInfo,
- mWifiStateMachine.syncGetScanResultsList());
- }
- }
- }, filter);
-
- // Setting is in seconds
- NOTIFICATION_REPEAT_DELAY_MS = Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
- mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler());
- mNotificationEnabledSettingObserver.register();
- }
-
- private synchronized void checkAndSetNotification(NetworkInfo networkInfo,
- List<ScanResult> scanResults) {
- // TODO: unregister broadcast so we do not have to check here
- // If we shouldn't place a notification on available networks, then
- // don't bother doing any of the following
- if (!mNotificationEnabled) return;
- if (networkInfo == null) return;
- if (mWifiState != WifiManager.WIFI_STATE_ENABLED) return;
-
- NetworkInfo.State state = networkInfo.getState();
- if ((state == NetworkInfo.State.DISCONNECTED)
- || (state == NetworkInfo.State.UNKNOWN)) {
- if (scanResults != null) {
- int numOpenNetworks = 0;
- for (int i = scanResults.size() - 1; i >= 0; i--) {
- ScanResult scanResult = scanResults.get(i);
-
- //A capability of [ESS] represents an open access point
- //that is available for an STA to connect
- if (scanResult.capabilities != null &&
- scanResult.capabilities.equals("[ESS]")) {
- numOpenNetworks++;
- }
- }
-
- if (numOpenNetworks > 0) {
- if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
- /*
- * We've scanned continuously at least
- * NUM_SCANS_BEFORE_NOTIFICATION times. The user
- * probably does not have a remembered network in range,
- * since otherwise supplicant would have tried to
- * associate and thus resetting this counter.
- */
- setNotificationVisible(true, numOpenNetworks, false, 0);
- }
- return;
- }
- }
- }
-
- // No open networks in range, remove the notification
- setNotificationVisible(false, 0, false, 0);
- }
-
- /**
- * Clears variables related to tracking whether a notification has been
- * shown recently and clears the current notification.
- */
- private synchronized void resetNotification() {
- mNotificationRepeatTime = 0;
- mNumScansSinceNetworkStateChange = 0;
- setNotificationVisible(false, 0, false, 0);
- }
-
- /**
- * Display or don't display a notification that there are open Wi-Fi networks.
- * @param visible {@code true} if notification should be visible, {@code false} otherwise
- * @param numNetworks the number networks seen
- * @param force {@code true} to force notification to be shown/not-shown,
- * even if it is already shown/not-shown.
- * @param delay time in milliseconds after which the notification should be made
- * visible or invisible.
- */
- private void setNotificationVisible(boolean visible, int numNetworks, boolean force,
- int delay) {
-
- // Since we use auto cancel on the notification, when the
- // mNetworksAvailableNotificationShown is true, the notification may
- // have actually been canceled. However, when it is false we know
- // for sure that it is not being shown (it will not be shown any other
- // place than here)
-
- // If it should be hidden and it is already hidden, then noop
- if (!visible && !mNotificationShown && !force) {
- return;
- }
-
- NotificationManager notificationManager = (NotificationManager) mContext
- .getSystemService(Context.NOTIFICATION_SERVICE);
-
- Message message;
- if (visible) {
-
- // Not enough time has passed to show the notification again
- if (System.currentTimeMillis() < mNotificationRepeatTime) {
- return;
- }
-
- if (mNotification == null) {
- // Cache the Notification object.
- mNotification = new Notification();
- mNotification.when = 0;
- mNotification.icon = ICON_NETWORKS_AVAILABLE;
- mNotification.flags = Notification.FLAG_AUTO_CANCEL;
- mNotification.contentIntent = TaskStackBuilder.create(mContext)
- .addNextIntentWithParentStack(
- new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK))
- .getPendingIntent(0, 0, null, UserHandle.CURRENT);
- }
-
- CharSequence title = mContext.getResources().getQuantityText(
- com.android.internal.R.plurals.wifi_available, numNetworks);
- CharSequence details = mContext.getResources().getQuantityText(
- com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
- mNotification.tickerText = title;
- mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
-
- mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
-
- notificationManager.notifyAsUser(null, ICON_NETWORKS_AVAILABLE, mNotification,
- UserHandle.ALL);
- } else {
- notificationManager.cancelAsUser(null, ICON_NETWORKS_AVAILABLE, UserHandle.ALL);
- }
-
- mNotificationShown = visible;
- }
-
- void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("mNotificationEnabled " + mNotificationEnabled);
- pw.println("mNotificationRepeatTime " + mNotificationRepeatTime);
- pw.println("mNotificationShown " + mNotificationShown);
- pw.println("mNumScansSinceNetworkStateChange " + mNumScansSinceNetworkStateChange);
- }
-
- private class NotificationEnabledSettingObserver extends ContentObserver {
- public NotificationEnabledSettingObserver(Handler handler) {
- super(handler);
- }
-
- public void register() {
- ContentResolver cr = mContext.getContentResolver();
- cr.registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
- synchronized (WifiNotificationController.this) {
- mNotificationEnabled = getValue();
- }
- }
-
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
-
- synchronized (WifiNotificationController.this) {
- mNotificationEnabled = getValue();
- resetNotification();
- }
- }
-
- private boolean getValue() {
- return Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
- }
- }
-
-}
diff --git a/services/core/java/com/android/server/wifi/WifiService.java b/services/core/java/com/android/server/wifi/WifiService.java
deleted file mode 100644
index f2efde1..0000000
--- a/services/core/java/com/android/server/wifi/WifiService.java
+++ /dev/null
@@ -1,1599 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wifi;
-
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.bluetooth.BluetoothAdapter;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.database.ContentObserver;
-import android.net.DhcpInfo;
-import android.net.DhcpResults;
-import android.net.LinkAddress;
-import android.net.NetworkUtils;
-import android.net.RouteInfo;
-import android.net.wifi.IWifiManager;
-import android.net.wifi.ScanResult;
-import android.net.wifi.BatchedScanResult;
-import android.net.wifi.BatchedScanSettings;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiConfiguration.ProxySettings;
-import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
-import android.net.wifi.WifiStateMachine;
-import android.net.wifi.WifiWatchdogStateMachine;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.Messenger;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.INetworkManagementService;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.os.WorkSource;
-import android.os.AsyncTask;
-import android.provider.Settings;
-import android.util.Log;
-import android.util.Slog;
-
-import java.io.FileNotFoundException;
-import java.io.BufferedReader;
-import java.io.FileDescriptor;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.PrintWriter;
-
-import java.net.InetAddress;
-import java.net.Inet4Address;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import com.android.internal.R;
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.util.AsyncChannel;
-import com.android.server.am.BatteryStatsService;
-import static com.android.server.wifi.WifiController.CMD_AIRPLANE_TOGGLED;
-import static com.android.server.wifi.WifiController.CMD_BATTERY_CHANGED;
-import static com.android.server.wifi.WifiController.CMD_EMERGENCY_MODE_CHANGED;
-import static com.android.server.wifi.WifiController.CMD_LOCKS_CHANGED;
-import static com.android.server.wifi.WifiController.CMD_SCAN_ALWAYS_MODE_CHANGED;
-import static com.android.server.wifi.WifiController.CMD_SCREEN_OFF;
-import static com.android.server.wifi.WifiController.CMD_SCREEN_ON;
-import static com.android.server.wifi.WifiController.CMD_SET_AP;
-import static com.android.server.wifi.WifiController.CMD_USER_PRESENT;
-import static com.android.server.wifi.WifiController.CMD_WIFI_TOGGLED;
-/**
- * WifiService handles remote WiFi operation requests by implementing
- * the IWifiManager interface.
- *
- * @hide
- */
-public final class WifiService extends IWifiManager.Stub {
- private static final String TAG = "WifiService";
- private static final boolean DBG = false;
-
- final WifiStateMachine mWifiStateMachine;
-
- private final Context mContext;
-
- final LockList mLocks = new LockList();
- // some wifi lock statistics
- private int mFullHighPerfLocksAcquired;
- private int mFullHighPerfLocksReleased;
- private int mFullLocksAcquired;
- private int mFullLocksReleased;
- private int mScanLocksAcquired;
- private int mScanLocksReleased;
-
- private final List<Multicaster> mMulticasters =
- new ArrayList<Multicaster>();
- private int mMulticastEnabled;
- private int mMulticastDisabled;
-
- private final IBatteryStats mBatteryStats;
- private final AppOpsManager mAppOps;
-
- private String mInterfaceName;
-
- /* Tracks the open wi-fi network notification */
- private WifiNotificationController mNotificationController;
- /* Polls traffic stats and notifies clients */
- private WifiTrafficPoller mTrafficPoller;
- /* Tracks the persisted states for wi-fi & airplane mode */
- final WifiSettingsStore mSettingsStore;
-
- final boolean mBatchedScanSupported;
-
- /**
- * Asynchronous channel to WifiStateMachine
- */
- private AsyncChannel mWifiStateMachineChannel;
-
- /**
- * Handles client connections
- */
- private class ClientHandler extends Handler {
-
- ClientHandler(android.os.Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
- if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
- if (DBG) Slog.d(TAG, "New client listening to asynchronous messages");
- // We track the clients by the Messenger
- // since it is expected to be always available
- mTrafficPoller.addClient(msg.replyTo);
- } else {
- Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
- }
- break;
- }
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
- if (msg.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
- if (DBG) Slog.d(TAG, "Send failed, client connection lost");
- } else {
- if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
- }
- mTrafficPoller.removeClient(msg.replyTo);
- break;
- }
- case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
- AsyncChannel ac = new AsyncChannel();
- ac.connect(mContext, this, msg.replyTo);
- break;
- }
- /* Client commands are forwarded to state machine */
- case WifiManager.CONNECT_NETWORK:
- case WifiManager.SAVE_NETWORK: {
- WifiConfiguration config = (WifiConfiguration) msg.obj;
- int networkId = msg.arg1;
- if (config != null && config.isValid()) {
- // This is restricted because there is no UI for the user to
- // monitor/control PAC.
- if (config.proxySettings != ProxySettings.PAC) {
- if (DBG) Slog.d(TAG, "Connect with config" + config);
- mWifiStateMachine.sendMessage(Message.obtain(msg));
- } else {
- Slog.e(TAG, "ClientHandler.handleMessage cannot process msg with PAC");
- if (msg.what == WifiManager.CONNECT_NETWORK) {
- replyFailed(msg, WifiManager.CONNECT_NETWORK_FAILED);
- } else {
- replyFailed(msg, WifiManager.SAVE_NETWORK_FAILED);
- }
- }
- } else if (config == null
- && networkId != WifiConfiguration.INVALID_NETWORK_ID) {
- if (DBG) Slog.d(TAG, "Connect with networkId" + networkId);
- mWifiStateMachine.sendMessage(Message.obtain(msg));
- } else {
- Slog.e(TAG, "ClientHandler.handleMessage ignoring invalid msg=" + msg);
- if (msg.what == WifiManager.CONNECT_NETWORK) {
- replyFailed(msg, WifiManager.CONNECT_NETWORK_FAILED);
- } else {
- replyFailed(msg, WifiManager.SAVE_NETWORK_FAILED);
- }
- }
- break;
- }
- case WifiManager.FORGET_NETWORK:
- case WifiManager.START_WPS:
- case WifiManager.CANCEL_WPS:
- case WifiManager.DISABLE_NETWORK:
- case WifiManager.RSSI_PKTCNT_FETCH: {
- mWifiStateMachine.sendMessage(Message.obtain(msg));
- break;
- }
- default: {
- Slog.d(TAG, "ClientHandler.handleMessage ignoring msg=" + msg);
- break;
- }
- }
- }
-
- private void replyFailed(Message msg, int what) {
- Message reply = msg.obtain();
- reply.what = what;
- reply.arg1 = WifiManager.INVALID_ARGS;
- try {
- msg.replyTo.send(reply);
- } catch (RemoteException e) {
- // There's not much we can do if reply can't be sent!
- }
- }
- }
- private ClientHandler mClientHandler;
-
- /**
- * Handles interaction with WifiStateMachine
- */
- private class WifiStateMachineHandler extends Handler {
- private AsyncChannel mWsmChannel;
-
- WifiStateMachineHandler(android.os.Looper looper) {
- super(looper);
- mWsmChannel = new AsyncChannel();
- mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler());
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
- if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
- mWifiStateMachineChannel = mWsmChannel;
- } else {
- Slog.e(TAG, "WifiStateMachine connection failure, error=" + msg.arg1);
- mWifiStateMachineChannel = null;
- }
- break;
- }
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
- Slog.e(TAG, "WifiStateMachine channel lost, msg.arg1 =" + msg.arg1);
- mWifiStateMachineChannel = null;
- //Re-establish connection to state machine
- mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler());
- break;
- }
- default: {
- Slog.d(TAG, "WifiStateMachineHandler.handleMessage ignoring msg=" + msg);
- break;
- }
- }
- }
- }
- WifiStateMachineHandler mWifiStateMachineHandler;
-
- private WifiWatchdogStateMachine mWifiWatchdogStateMachine;
-
- public WifiService(Context context) {
- mContext = context;
-
- mInterfaceName = SystemProperties.get("wifi.interface", "wlan0");
-
- mWifiStateMachine = new WifiStateMachine(mContext, mInterfaceName);
- mWifiStateMachine.enableRssiPolling(true);
- mBatteryStats = BatteryStatsService.getService();
- mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
-
- mNotificationController = new WifiNotificationController(mContext, mWifiStateMachine);
- mTrafficPoller = new WifiTrafficPoller(mContext, mInterfaceName);
- mSettingsStore = new WifiSettingsStore(mContext);
-
- HandlerThread wifiThread = new HandlerThread("WifiService");
- wifiThread.start();
- mClientHandler = new ClientHandler(wifiThread.getLooper());
- mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper());
- mWifiController = new WifiController(mContext, this, wifiThread.getLooper());
- mWifiController.start();
-
- mBatchedScanSupported = mContext.getResources().getBoolean(
- R.bool.config_wifi_batched_scan_supported);
-
- registerForScanModeChange();
- mContext.registerReceiver(
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (mSettingsStore.handleAirplaneModeToggled()) {
- mWifiController.sendMessage(CMD_AIRPLANE_TOGGLED);
- }
- }
- },
- new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
-
- // Adding optimizations of only receiving broadcasts when wifi is enabled
- // can result in race conditions when apps toggle wifi in the background
- // without active user involvement. Always receive broadcasts.
- registerForBroadcasts();
- }
-
- private WifiController mWifiController;
-
- /**
- * Check if Wi-Fi needs to be enabled and start
- * if needed
- *
- * This function is used only at boot time
- */
- public void checkAndStartWifi() {
- /* Check if wi-fi needs to be enabled */
- boolean wifiEnabled = mSettingsStore.isWifiToggleEnabled();
- Slog.i(TAG, "WifiService starting up with Wi-Fi " +
- (wifiEnabled ? "enabled" : "disabled"));
-
- // If we are already disabled (could be due to airplane mode), avoid changing persist
- // state here
- if (wifiEnabled) setWifiEnabled(wifiEnabled);
-
- mWifiWatchdogStateMachine = WifiWatchdogStateMachine.
- makeWifiWatchdogStateMachine(mContext);
-
- }
-
- /**
- * see {@link android.net.wifi.WifiManager#pingSupplicant()}
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public boolean pingSupplicant() {
- enforceAccessPermission();
- if (mWifiStateMachineChannel != null) {
- return mWifiStateMachine.syncPingSupplicant(mWifiStateMachineChannel);
- } else {
- Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
- return false;
- }
- }
-
- /**
- * see {@link android.net.wifi.WifiManager#startScan()}
- *
- * <p>If workSource is null, all blame is given to the calling uid.
- */
- public void startScan(WorkSource workSource) {
- enforceChangePermission();
- if (workSource != null) {
- enforceWorkSourcePermission();
- // WifiManager currently doesn't use names, so need to clear names out of the
- // supplied WorkSource to allow future WorkSource combining.
- workSource.clearNames();
- }
- mWifiStateMachine.startScan(Binder.getCallingUid(), workSource);
- }
-
- private class BatchedScanRequest extends DeathRecipient {
- final BatchedScanSettings settings;
- final int uid;
- final int pid;
- final WorkSource workSource;
-
- BatchedScanRequest(BatchedScanSettings settings, IBinder binder, WorkSource ws) {
- super(0, null, binder, null);
- this.settings = settings;
- this.uid = getCallingUid();
- this.pid = getCallingPid();
- workSource = ws;
- }
- public void binderDied() {
- stopBatchedScan(settings, uid, pid);
- }
- public String toString() {
- return "BatchedScanRequest{settings=" + settings + ", binder=" + mBinder + "}";
- }
-
- public boolean isSameApp(int uid, int pid) {
- return (this.uid == uid && this.pid == pid);
- }
- }
-
- private final List<BatchedScanRequest> mBatchedScanners = new ArrayList<BatchedScanRequest>();
-
- public boolean isBatchedScanSupported() {
- return mBatchedScanSupported;
- }
-
- public void pollBatchedScan() {
- enforceChangePermission();
- if (mBatchedScanSupported == false) return;
- mWifiStateMachine.requestBatchedScanPoll();
- }
-
- /**
- * see {@link android.net.wifi.WifiManager#requestBatchedScan()}
- */
- public boolean requestBatchedScan(BatchedScanSettings requested, IBinder binder,
- WorkSource workSource) {
- enforceChangePermission();
- if (workSource != null) {
- enforceWorkSourcePermission();
- // WifiManager currently doesn't use names, so need to clear names out of the
- // supplied WorkSource to allow future WorkSource combining.
- workSource.clearNames();
- }
- if (mBatchedScanSupported == false) return false;
- requested = new BatchedScanSettings(requested);
- if (requested.isInvalid()) return false;
- BatchedScanRequest r = new BatchedScanRequest(requested, binder, workSource);
- synchronized(mBatchedScanners) {
- mBatchedScanners.add(r);
- resolveBatchedScannersLocked();
- }
- return true;
- }
-
- public List<BatchedScanResult> getBatchedScanResults(String callingPackage) {
- enforceAccessPermission();
- if (mBatchedScanSupported == false) return new ArrayList<BatchedScanResult>();
- int userId = UserHandle.getCallingUserId();
- int uid = Binder.getCallingUid();
- long ident = Binder.clearCallingIdentity();
- try {
- if (mAppOps.noteOp(AppOpsManager.OP_WIFI_SCAN, uid, callingPackage)
- != AppOpsManager.MODE_ALLOWED) {
- return new ArrayList<BatchedScanResult>();
- }
- int currentUser = ActivityManager.getCurrentUser();
- if (userId != currentUser) {
- return new ArrayList<BatchedScanResult>();
- } else {
- return mWifiStateMachine.syncGetBatchedScanResultsList();
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
-
- public void stopBatchedScan(BatchedScanSettings settings) {
- enforceChangePermission();
- if (mBatchedScanSupported == false) return;
- stopBatchedScan(settings, getCallingUid(), getCallingPid());
- }
-
- private void stopBatchedScan(BatchedScanSettings settings, int uid, int pid) {
- ArrayList<BatchedScanRequest> found = new ArrayList<BatchedScanRequest>();
- synchronized(mBatchedScanners) {
- for (BatchedScanRequest r : mBatchedScanners) {
- if (r.isSameApp(uid, pid) && (settings == null || settings.equals(r.settings))) {
- found.add(r);
- if (settings != null) break;
- }
- }
- for (BatchedScanRequest r : found) {
- mBatchedScanners.remove(r);
- }
- if (found.size() != 0) {
- resolveBatchedScannersLocked();
- }
- }
- }
-
- private void resolveBatchedScannersLocked() {
- BatchedScanSettings setting = new BatchedScanSettings();
- WorkSource responsibleWorkSource = null;
- int responsibleUid = 0;
- double responsibleCsph = 0; // Channel Scans Per Hour
-
- if (mBatchedScanners.size() == 0) {
- mWifiStateMachine.setBatchedScanSettings(null, 0, 0, null);
- return;
- }
- for (BatchedScanRequest r : mBatchedScanners) {
- BatchedScanSettings s = r.settings;
-
- // evaluate responsibility
- int currentChannelCount;
- int currentScanInterval;
- double currentCsph;
-
- if (s.channelSet == null || s.channelSet.isEmpty()) {
- // all channels - 11 B and 9 A channels roughly.
- currentChannelCount = 9 + 11;
- } else {
- currentChannelCount = s.channelSet.size();
- // these are rough est - no real need to correct for reg-domain;
- if (s.channelSet.contains("A")) currentChannelCount += (9 - 1);
- if (s.channelSet.contains("B")) currentChannelCount += (11 - 1);
-
- }
- if (s.scanIntervalSec == BatchedScanSettings.UNSPECIFIED) {
- currentScanInterval = BatchedScanSettings.DEFAULT_INTERVAL_SEC;
- } else {
- currentScanInterval = s.scanIntervalSec;
- }
- currentCsph = 60 * 60 * currentChannelCount / currentScanInterval;
-
- if (currentCsph > responsibleCsph) {
- responsibleUid = r.uid;
- responsibleWorkSource = r.workSource;
- responsibleCsph = currentCsph;
- }
-
- if (s.maxScansPerBatch != BatchedScanSettings.UNSPECIFIED &&
- s.maxScansPerBatch < setting.maxScansPerBatch) {
- setting.maxScansPerBatch = s.maxScansPerBatch;
- }
- if (s.maxApPerScan != BatchedScanSettings.UNSPECIFIED &&
- (setting.maxApPerScan == BatchedScanSettings.UNSPECIFIED ||
- s.maxApPerScan > setting.maxApPerScan)) {
- setting.maxApPerScan = s.maxApPerScan;
- }
- if (s.scanIntervalSec != BatchedScanSettings.UNSPECIFIED &&
- s.scanIntervalSec < setting.scanIntervalSec) {
- setting.scanIntervalSec = s.scanIntervalSec;
- }
- if (s.maxApForDistance != BatchedScanSettings.UNSPECIFIED &&
- (setting.maxApForDistance == BatchedScanSettings.UNSPECIFIED ||
- s.maxApForDistance > setting.maxApForDistance)) {
- setting.maxApForDistance = s.maxApForDistance;
- }
- if (s.channelSet != null && s.channelSet.size() != 0) {
- if (setting.channelSet == null || setting.channelSet.size() != 0) {
- if (setting.channelSet == null) setting.channelSet = new ArrayList<String>();
- for (String i : s.channelSet) {
- if (setting.channelSet.contains(i) == false) setting.channelSet.add(i);
- }
- } // else, ignore the constraint - we already use all channels
- } else {
- if (setting.channelSet == null || setting.channelSet.size() != 0) {
- setting.channelSet = new ArrayList<String>();
- }
- }
- }
-
- setting.constrain();
- mWifiStateMachine.setBatchedScanSettings(setting, responsibleUid, (int)responsibleCsph,
- responsibleWorkSource);
- }
-
- private void enforceAccessPermission() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
- "WifiService");
- }
-
- private void enforceChangePermission() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE,
- "WifiService");
-
- }
-
- private void enforceWorkSourcePermission() {
- mContext.enforceCallingPermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
- "WifiService");
-
- }
-
- private void enforceMulticastChangePermission() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE,
- "WifiService");
- }
-
- private void enforceConnectivityInternalPermission() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CONNECTIVITY_INTERNAL,
- "ConnectivityService");
- }
-
- /**
- * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)}
- * @param enable {@code true} to enable, {@code false} to disable.
- * @return {@code true} if the enable/disable operation was
- * started or is already in the queue.
- */
- public synchronized boolean setWifiEnabled(boolean enable) {
- enforceChangePermission();
- Slog.d(TAG, "setWifiEnabled: " + enable + " pid=" + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
- if (DBG) {
- Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n");
- }
-
- /*
- * Caller might not have WRITE_SECURE_SETTINGS,
- * only CHANGE_WIFI_STATE is enforced
- */
-
- long ident = Binder.clearCallingIdentity();
- try {
- if (! mSettingsStore.handleWifiToggled(enable)) {
- // Nothing to do if wifi cannot be toggled
- return true;
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
-
- mWifiController.sendMessage(CMD_WIFI_TOGGLED);
- return true;
- }
-
- /**
- * see {@link WifiManager#getWifiState()}
- * @return One of {@link WifiManager#WIFI_STATE_DISABLED},
- * {@link WifiManager#WIFI_STATE_DISABLING},
- * {@link WifiManager#WIFI_STATE_ENABLED},
- * {@link WifiManager#WIFI_STATE_ENABLING},
- * {@link WifiManager#WIFI_STATE_UNKNOWN}
- */
- public int getWifiEnabledState() {
- enforceAccessPermission();
- return mWifiStateMachine.syncGetWifiState();
- }
-
- /**
- * see {@link android.net.wifi.WifiManager#setWifiApEnabled(WifiConfiguration, boolean)}
- * @param wifiConfig SSID, security and channel details as
- * part of WifiConfiguration
- * @param enabled true to enable and false to disable
- */
- public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
- enforceChangePermission();
- // null wifiConfig is a meaningful input for CMD_SET_AP
- if (wifiConfig == null || wifiConfig.isValid()) {
- mWifiController.obtainMessage(CMD_SET_AP, enabled ? 1 : 0, 0, wifiConfig).sendToTarget();
- } else {
- Slog.e(TAG, "Invalid WifiConfiguration");
- }
- }
-
- /**
- * see {@link WifiManager#getWifiApState()}
- * @return One of {@link WifiManager#WIFI_AP_STATE_DISABLED},
- * {@link WifiManager#WIFI_AP_STATE_DISABLING},
- * {@link WifiManager#WIFI_AP_STATE_ENABLED},
- * {@link WifiManager#WIFI_AP_STATE_ENABLING},
- * {@link WifiManager#WIFI_AP_STATE_FAILED}
- */
- public int getWifiApEnabledState() {
- enforceAccessPermission();
- return mWifiStateMachine.syncGetWifiApState();
- }
-
- /**
- * see {@link WifiManager#getWifiApConfiguration()}
- * @return soft access point configuration
- */
- public WifiConfiguration getWifiApConfiguration() {
- enforceAccessPermission();
- return mWifiStateMachine.syncGetWifiApConfiguration();
- }
-
- /**
- * see {@link WifiManager#setWifiApConfiguration(WifiConfiguration)}
- * @param wifiConfig WifiConfiguration details for soft access point
- */
- public void setWifiApConfiguration(WifiConfiguration wifiConfig) {
- enforceChangePermission();
- if (wifiConfig == null)
- return;
- if (wifiConfig.isValid()) {
- mWifiStateMachine.setWifiApConfiguration(wifiConfig);
- } else {
- Slog.e(TAG, "Invalid WifiConfiguration");
- }
- }
-
- /**
- * @param enable {@code true} to enable, {@code false} to disable.
- * @return {@code true} if the enable/disable operation was
- * started or is already in the queue.
- */
- public boolean isScanAlwaysAvailable() {
- enforceAccessPermission();
- return mSettingsStore.isScanAlwaysAvailable();
- }
-
- /**
- * see {@link android.net.wifi.WifiManager#disconnect()}
- */
- public void disconnect() {
- enforceChangePermission();
- mWifiStateMachine.disconnectCommand();
- }
-
- /**
- * see {@link android.net.wifi.WifiManager#reconnect()}
- */
- public void reconnect() {
- enforceChangePermission();
- mWifiStateMachine.reconnectCommand();
- }
-
- /**
- * see {@link android.net.wifi.WifiManager#reassociate()}
- */
- public void reassociate() {
- enforceChangePermission();
- mWifiStateMachine.reassociateCommand();
- }
-
- /**
- * see {@link android.net.wifi.WifiManager#getConfiguredNetworks()}
- * @return the list of configured networks
- */
- public List<WifiConfiguration> getConfiguredNetworks() {
- enforceAccessPermission();
- if (mWifiStateMachineChannel != null) {
- return mWifiStateMachine.syncGetConfiguredNetworks(mWifiStateMachineChannel);
- } else {
- Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
- return null;
- }
- }
-
- /**
- * see {@link android.net.wifi.WifiManager#addOrUpdateNetwork(WifiConfiguration)}
- * @return the supplicant-assigned identifier for the new or updated
- * network if the operation succeeds, or {@code -1} if it fails
- */
- public int addOrUpdateNetwork(WifiConfiguration config) {
- enforceChangePermission();
- if (config.proxySettings == ProxySettings.PAC) {
- enforceConnectivityInternalPermission();
- }
- if (config.isValid()) {
- if (mWifiStateMachineChannel != null) {
- return mWifiStateMachine.syncAddOrUpdateNetwork(mWifiStateMachineChannel, config);
- } else {
- Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
- return -1;
- }
- } else {
- Slog.e(TAG, "bad network configuration");
- return -1;
- }
- }
-
- /**
- * See {@link android.net.wifi.WifiManager#removeNetwork(int)}
- * @param netId the integer that identifies the network configuration
- * to the supplicant
- * @return {@code true} if the operation succeeded
- */
- public boolean removeNetwork(int netId) {
- enforceChangePermission();
- if (mWifiStateMachineChannel != null) {
- return mWifiStateMachine.syncRemoveNetwork(mWifiStateMachineChannel, netId);
- } else {
- Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
- return false;
- }
- }
-
- /**
- * See {@link android.net.wifi.WifiManager#enableNetwork(int, boolean)}
- * @param netId the integer that identifies the network configuration
- * to the supplicant
- * @param disableOthers if true, disable all other networks.
- * @return {@code true} if the operation succeeded
- */
- public boolean enableNetwork(int netId, boolean disableOthers) {
- enforceChangePermission();
- if (mWifiStateMachineChannel != null) {
- return mWifiStateMachine.syncEnableNetwork(mWifiStateMachineChannel, netId,
- disableOthers);
- } else {
- Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
- return false;
- }
- }
-
- /**
- * See {@link android.net.wifi.WifiManager#disableNetwork(int)}
- * @param netId the integer that identifies the network configuration
- * to the supplicant
- * @return {@code true} if the operation succeeded
- */
- public boolean disableNetwork(int netId) {
- enforceChangePermission();
- if (mWifiStateMachineChannel != null) {
- return mWifiStateMachine.syncDisableNetwork(mWifiStateMachineChannel, netId);
- } else {
- Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
- return false;
- }
- }
-
- /**
- * See {@link android.net.wifi.WifiManager#getConnectionInfo()}
- * @return the Wi-Fi information, contained in {@link WifiInfo}.
- */
- public WifiInfo getConnectionInfo() {
- enforceAccessPermission();
- /*
- * Make sure we have the latest information, by sending
- * a status request to the supplicant.
- */
- return mWifiStateMachine.syncRequestConnectionInfo();
- }
-
- /**
- * Return the results of the most recent access point scan, in the form of
- * a list of {@link ScanResult} objects.
- * @return the list of results
- */
- public List<ScanResult> getScanResults(String callingPackage) {
- enforceAccessPermission();
- int userId = UserHandle.getCallingUserId();
- int uid = Binder.getCallingUid();
- long ident = Binder.clearCallingIdentity();
- try {
- if (mAppOps.noteOp(AppOpsManager.OP_WIFI_SCAN, uid, callingPackage)
- != AppOpsManager.MODE_ALLOWED) {
- return new ArrayList<ScanResult>();
- }
- int currentUser = ActivityManager.getCurrentUser();
- if (userId != currentUser) {
- return new ArrayList<ScanResult>();
- } else {
- return mWifiStateMachine.syncGetScanResultsList();
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- /**
- * Tell the supplicant to persist the current list of configured networks.
- * @return {@code true} if the operation succeeded
- *
- * TODO: deprecate this
- */
- public boolean saveConfiguration() {
- boolean result = true;
- enforceChangePermission();
- if (mWifiStateMachineChannel != null) {
- return mWifiStateMachine.syncSaveConfig(mWifiStateMachineChannel);
- } else {
- Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
- return false;
- }
- }
-
- /**
- * Set the country code
- * @param countryCode ISO 3166 country code.
- * @param persist {@code true} if the setting should be remembered.
- *
- * The persist behavior exists so that wifi can fall back to the last
- * persisted country code on a restart, when the locale information is
- * not available from telephony.
- */
- public void setCountryCode(String countryCode, boolean persist) {
- Slog.i(TAG, "WifiService trying to set country code to " + countryCode +
- " with persist set to " + persist);
- enforceChangePermission();
- final long token = Binder.clearCallingIdentity();
- try {
- mWifiStateMachine.setCountryCode(countryCode, persist);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Set the operational frequency band
- * @param band One of
- * {@link WifiManager#WIFI_FREQUENCY_BAND_AUTO},
- * {@link WifiManager#WIFI_FREQUENCY_BAND_5GHZ},
- * {@link WifiManager#WIFI_FREQUENCY_BAND_2GHZ},
- * @param persist {@code true} if the setting should be remembered.
- *
- */
- public void setFrequencyBand(int band, boolean persist) {
- enforceChangePermission();
- if (!isDualBandSupported()) return;
- Slog.i(TAG, "WifiService trying to set frequency band to " + band +
- " with persist set to " + persist);
- final long token = Binder.clearCallingIdentity();
- try {
- mWifiStateMachine.setFrequencyBand(band, persist);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
-
- /**
- * Get the operational frequency band
- */
- public int getFrequencyBand() {
- enforceAccessPermission();
- return mWifiStateMachine.getFrequencyBand();
- }
-
- public boolean isDualBandSupported() {
- //TODO: Should move towards adding a driver API that checks at runtime
- return mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_wifi_dual_band_support);
- }
-
- /**
- * Return the DHCP-assigned addresses from the last successful DHCP request,
- * if any.
- * @return the DHCP information
- * @deprecated
- */
- public DhcpInfo getDhcpInfo() {
- enforceAccessPermission();
- DhcpResults dhcpResults = mWifiStateMachine.syncGetDhcpResults();
- if (dhcpResults.linkProperties == null) return null;
-
- DhcpInfo info = new DhcpInfo();
- for (LinkAddress la : dhcpResults.linkProperties.getLinkAddresses()) {
- InetAddress addr = la.getAddress();
- if (addr instanceof Inet4Address) {
- info.ipAddress = NetworkUtils.inetAddressToInt((Inet4Address)addr);
- break;
- }
- }
- for (RouteInfo r : dhcpResults.linkProperties.getRoutes()) {
- if (r.isDefaultRoute()) {
- InetAddress gateway = r.getGateway();
- if (gateway instanceof Inet4Address) {
- info.gateway = NetworkUtils.inetAddressToInt((Inet4Address)gateway);
- }
- } else if (r.hasGateway() == false) {
- LinkAddress dest = r.getDestination();
- if (dest.getAddress() instanceof Inet4Address) {
- info.netmask = NetworkUtils.prefixLengthToNetmaskInt(
- dest.getNetworkPrefixLength());
- }
- }
- }
- int dnsFound = 0;
- for (InetAddress dns : dhcpResults.linkProperties.getDnses()) {
- if (dns instanceof Inet4Address) {
- if (dnsFound == 0) {
- info.dns1 = NetworkUtils.inetAddressToInt((Inet4Address)dns);
- } else {
- info.dns2 = NetworkUtils.inetAddressToInt((Inet4Address)dns);
- }
- if (++dnsFound > 1) break;
- }
- }
- InetAddress serverAddress = dhcpResults.serverAddress;
- if (serverAddress instanceof Inet4Address) {
- info.serverAddress = NetworkUtils.inetAddressToInt((Inet4Address)serverAddress);
- }
- info.leaseDuration = dhcpResults.leaseDuration;
-
- return info;
- }
-
- /**
- * see {@link android.net.wifi.WifiManager#startWifi}
- *
- */
- public void startWifi() {
- enforceConnectivityInternalPermission();
- /* TODO: may be add permissions for access only to connectivity service
- * TODO: if a start issued, keep wifi alive until a stop issued irrespective
- * of WifiLock & device idle status unless wifi enabled status is toggled
- */
-
- mWifiStateMachine.setDriverStart(true);
- mWifiStateMachine.reconnectCommand();
- }
-
- public void captivePortalCheckComplete() {
- enforceConnectivityInternalPermission();
- mWifiStateMachine.captivePortalCheckComplete();
- }
-
- /**
- * see {@link android.net.wifi.WifiManager#stopWifi}
- *
- */
- public void stopWifi() {
- enforceConnectivityInternalPermission();
- /*
- * TODO: if a stop is issued, wifi is brought up only by startWifi
- * unless wifi enabled status is toggled
- */
- mWifiStateMachine.setDriverStart(false);
- }
-
- /**
- * see {@link android.net.wifi.WifiManager#addToBlacklist}
- *
- */
- public void addToBlacklist(String bssid) {
- enforceChangePermission();
-
- mWifiStateMachine.addToBlacklist(bssid);
- }
-
- /**
- * see {@link android.net.wifi.WifiManager#clearBlacklist}
- *
- */
- public void clearBlacklist() {
- enforceChangePermission();
-
- mWifiStateMachine.clearBlacklist();
- }
-
- /**
- * enable TDLS for the local NIC to remote NIC
- * The APPs don't know the remote MAC address to identify NIC though,
- * so we need to do additional work to find it from remote IP address
- */
-
- class TdlsTaskParams {
- public String remoteIpAddress;
- public boolean enable;
- }
-
- class TdlsTask extends AsyncTask<TdlsTaskParams, Integer, Integer> {
- @Override
- protected Integer doInBackground(TdlsTaskParams... params) {
-
- // Retrieve parameters for the call
- TdlsTaskParams param = params[0];
- String remoteIpAddress = param.remoteIpAddress.trim();
- boolean enable = param.enable;
-
- // Get MAC address of Remote IP
- String macAddress = null;
-
- BufferedReader reader = null;
-
- try {
- reader = new BufferedReader(new FileReader("/proc/net/arp"));
-
- // Skip over the line bearing colum titles
- String line = reader.readLine();
-
- while ((line = reader.readLine()) != null) {
- String[] tokens = line.split("[ ]+");
- if (tokens.length < 6) {
- continue;
- }
-
- // ARP column format is
- // Address HWType HWAddress Flags Mask IFace
- String ip = tokens[0];
- String mac = tokens[3];
-
- if (remoteIpAddress.equals(ip)) {
- macAddress = mac;
- break;
- }
- }
-
- if (macAddress == null) {
- Slog.w(TAG, "Did not find remoteAddress {" + remoteIpAddress + "} in " +
- "/proc/net/arp");
- } else {
- enableTdlsWithMacAddress(macAddress, enable);
- }
-
- } catch (FileNotFoundException e) {
- Slog.e(TAG, "Could not open /proc/net/arp to lookup mac address");
- } catch (IOException e) {
- Slog.e(TAG, "Could not read /proc/net/arp to lookup mac address");
- } finally {
- try {
- if (reader != null) {
- reader.close();
- }
- }
- catch (IOException e) {
- // Do nothing
- }
- }
-
- return 0;
- }
- }
-
- public void enableTdls(String remoteAddress, boolean enable) {
- TdlsTaskParams params = new TdlsTaskParams();
- params.remoteIpAddress = remoteAddress;
- params.enable = enable;
- new TdlsTask().execute(params);
- }
-
-
- public void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable) {
- mWifiStateMachine.enableTdls(remoteMacAddress, enable);
- }
-
- /**
- * Get a reference to handler. This is used by a client to establish
- * an AsyncChannel communication with WifiService
- */
- public Messenger getWifiServiceMessenger() {
- enforceAccessPermission();
- enforceChangePermission();
- return new Messenger(mClientHandler);
- }
-
- /** Get a reference to WifiStateMachine handler for AsyncChannel communication */
- public Messenger getWifiStateMachineMessenger() {
- enforceAccessPermission();
- enforceChangePermission();
- return mWifiStateMachine.getMessenger();
- }
-
- /**
- * Get the IP and proxy configuration file
- */
- public String getConfigFile() {
- enforceAccessPermission();
- return mWifiStateMachine.getConfigFile();
- }
-
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(Intent.ACTION_SCREEN_ON)) {
- mWifiController.sendMessage(CMD_SCREEN_ON);
- } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
- mWifiController.sendMessage(CMD_USER_PRESENT);
- } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
- mWifiController.sendMessage(CMD_SCREEN_OFF);
- } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
- int pluggedType = intent.getIntExtra("plugged", 0);
- mWifiController.sendMessage(CMD_BATTERY_CHANGED, pluggedType, 0, null);
- } else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
- int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
- BluetoothAdapter.STATE_DISCONNECTED);
- mWifiStateMachine.sendBluetoothAdapterStateChange(state);
- } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
- boolean emergencyMode = intent.getBooleanExtra("phoneinECMState", false);
- mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, emergencyMode ? 1 : 0, 0);
- }
- }
- };
-
- /**
- * Observes settings changes to scan always mode.
- */
- private void registerForScanModeChange() {
- ContentObserver contentObserver = new ContentObserver(null) {
- @Override
- public void onChange(boolean selfChange) {
- mSettingsStore.handleWifiScanAlwaysAvailableToggled();
- mWifiController.sendMessage(CMD_SCAN_ALWAYS_MODE_CHANGED);
- }
- };
-
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE),
- false, contentObserver);
- }
-
- private void registerForBroadcasts() {
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_SCREEN_ON);
- intentFilter.addAction(Intent.ACTION_USER_PRESENT);
- intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
- intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
- intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
- intentFilter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
- intentFilter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
- mContext.registerReceiver(mReceiver, intentFilter);
- }
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump WifiService from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
- return;
- }
- pw.println("Wi-Fi is " + mWifiStateMachine.syncGetWifiStateByName());
- pw.println("Stay-awake conditions: " +
- Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0));
- pw.println("mMulticastEnabled " + mMulticastEnabled);
- pw.println("mMulticastDisabled " + mMulticastDisabled);
- mWifiController.dump(fd, pw, args);
- mSettingsStore.dump(fd, pw, args);
- mNotificationController.dump(fd, pw, args);
- mTrafficPoller.dump(fd, pw, args);
-
- pw.println("Latest scan results:");
- List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList();
- if (scanResults != null && scanResults.size() != 0) {
- pw.println(" BSSID Frequency RSSI Flags SSID");
- for (ScanResult r : scanResults) {
- pw.printf(" %17s %9d %5d %-16s %s%n",
- r.BSSID,
- r.frequency,
- r.level,
- r.capabilities,
- r.SSID == null ? "" : r.SSID);
- }
- }
- pw.println();
- pw.println("Locks acquired: " + mFullLocksAcquired + " full, " +
- mFullHighPerfLocksAcquired + " full high perf, " +
- mScanLocksAcquired + " scan");
- pw.println("Locks released: " + mFullLocksReleased + " full, " +
- mFullHighPerfLocksReleased + " full high perf, " +
- mScanLocksReleased + " scan");
- pw.println();
- pw.println("Locks held:");
- mLocks.dump(pw);
-
- mWifiWatchdogStateMachine.dump(fd, pw, args);
- pw.println();
- mWifiStateMachine.dump(fd, pw, args);
- pw.println();
- }
-
- private class WifiLock extends DeathRecipient {
- WifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) {
- super(lockMode, tag, binder, ws);
- }
-
- public void binderDied() {
- synchronized (mLocks) {
- releaseWifiLockLocked(mBinder);
- }
- }
-
- public String toString() {
- return "WifiLock{" + mTag + " type=" + mMode + " binder=" + mBinder + "}";
- }
- }
-
- class LockList {
- private List<WifiLock> mList;
-
- private LockList() {
- mList = new ArrayList<WifiLock>();
- }
-
- synchronized boolean hasLocks() {
- return !mList.isEmpty();
- }
-
- synchronized int getStrongestLockMode() {
- if (mList.isEmpty()) {
- return WifiManager.WIFI_MODE_FULL;
- }
-
- if (mFullHighPerfLocksAcquired > mFullHighPerfLocksReleased) {
- return WifiManager.WIFI_MODE_FULL_HIGH_PERF;
- }
-
- if (mFullLocksAcquired > mFullLocksReleased) {
- return WifiManager.WIFI_MODE_FULL;
- }
-
- return WifiManager.WIFI_MODE_SCAN_ONLY;
- }
-
- synchronized void updateWorkSource(WorkSource ws) {
- for (int i = 0; i < mLocks.mList.size(); i++) {
- ws.add(mLocks.mList.get(i).mWorkSource);
- }
- }
-
- private void addLock(WifiLock lock) {
- if (findLockByBinder(lock.mBinder) < 0) {
- mList.add(lock);
- }
- }
-
- private WifiLock removeLock(IBinder binder) {
- int index = findLockByBinder(binder);
- if (index >= 0) {
- WifiLock ret = mList.remove(index);
- ret.unlinkDeathRecipient();
- return ret;
- } else {
- return null;
- }
- }
-
- private int findLockByBinder(IBinder binder) {
- int size = mList.size();
- for (int i = size - 1; i >= 0; i--) {
- if (mList.get(i).mBinder == binder)
- return i;
- }
- return -1;
- }
-
- private void dump(PrintWriter pw) {
- for (WifiLock l : mList) {
- pw.print(" ");
- pw.println(l);
- }
- }
- }
-
- void enforceWakeSourcePermission(int uid, int pid) {
- if (uid == android.os.Process.myUid()) {
- return;
- }
- mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
- pid, uid, null);
- }
-
- public boolean acquireWifiLock(IBinder binder, int lockMode, String tag, WorkSource ws) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
- if (lockMode != WifiManager.WIFI_MODE_FULL &&
- lockMode != WifiManager.WIFI_MODE_SCAN_ONLY &&
- lockMode != WifiManager.WIFI_MODE_FULL_HIGH_PERF) {
- Slog.e(TAG, "Illegal argument, lockMode= " + lockMode);
- if (DBG) throw new IllegalArgumentException("lockMode=" + lockMode);
- return false;
- }
- if (ws != null && ws.size() == 0) {
- ws = null;
- }
- if (ws != null) {
- enforceWakeSourcePermission(Binder.getCallingUid(), Binder.getCallingPid());
- }
- if (ws == null) {
- ws = new WorkSource(Binder.getCallingUid());
- }
- WifiLock wifiLock = new WifiLock(lockMode, tag, binder, ws);
- synchronized (mLocks) {
- return acquireWifiLockLocked(wifiLock);
- }
- }
-
- private void noteAcquireWifiLock(WifiLock wifiLock) throws RemoteException {
- switch(wifiLock.mMode) {
- case WifiManager.WIFI_MODE_FULL:
- case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
- case WifiManager.WIFI_MODE_SCAN_ONLY:
- mBatteryStats.noteFullWifiLockAcquiredFromSource(wifiLock.mWorkSource);
- break;
- }
- }
-
- private void noteReleaseWifiLock(WifiLock wifiLock) throws RemoteException {
- switch(wifiLock.mMode) {
- case WifiManager.WIFI_MODE_FULL:
- case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
- case WifiManager.WIFI_MODE_SCAN_ONLY:
- mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource);
- break;
- }
- }
-
- private boolean acquireWifiLockLocked(WifiLock wifiLock) {
- if (DBG) Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock);
-
- mLocks.addLock(wifiLock);
-
- long ident = Binder.clearCallingIdentity();
- try {
- noteAcquireWifiLock(wifiLock);
- switch(wifiLock.mMode) {
- case WifiManager.WIFI_MODE_FULL:
- ++mFullLocksAcquired;
- break;
- case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
- ++mFullHighPerfLocksAcquired;
- break;
-
- case WifiManager.WIFI_MODE_SCAN_ONLY:
- ++mScanLocksAcquired;
- break;
- }
- mWifiController.sendMessage(CMD_LOCKS_CHANGED);
- return true;
- } catch (RemoteException e) {
- return false;
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- public void updateWifiLockWorkSource(IBinder lock, WorkSource ws) {
- int uid = Binder.getCallingUid();
- int pid = Binder.getCallingPid();
- if (ws != null && ws.size() == 0) {
- ws = null;
- }
- if (ws != null) {
- enforceWakeSourcePermission(uid, pid);
- }
- long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mLocks) {
- int index = mLocks.findLockByBinder(lock);
- if (index < 0) {
- throw new IllegalArgumentException("Wifi lock not active");
- }
- WifiLock wl = mLocks.mList.get(index);
- noteReleaseWifiLock(wl);
- wl.mWorkSource = ws != null ? new WorkSource(ws) : new WorkSource(uid);
- noteAcquireWifiLock(wl);
- }
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- public boolean releaseWifiLock(IBinder lock) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
- synchronized (mLocks) {
- return releaseWifiLockLocked(lock);
- }
- }
-
- private boolean releaseWifiLockLocked(IBinder lock) {
- boolean hadLock;
-
- WifiLock wifiLock = mLocks.removeLock(lock);
-
- if (DBG) Slog.d(TAG, "releaseWifiLockLocked: " + wifiLock);
-
- hadLock = (wifiLock != null);
-
- long ident = Binder.clearCallingIdentity();
- try {
- if (hadLock) {
- noteReleaseWifiLock(wifiLock);
- switch(wifiLock.mMode) {
- case WifiManager.WIFI_MODE_FULL:
- ++mFullLocksReleased;
- break;
- case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
- ++mFullHighPerfLocksReleased;
- break;
- case WifiManager.WIFI_MODE_SCAN_ONLY:
- ++mScanLocksReleased;
- break;
- }
- mWifiController.sendMessage(CMD_LOCKS_CHANGED);
- }
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
-
- return hadLock;
- }
-
- private abstract class DeathRecipient
- implements IBinder.DeathRecipient {
- String mTag;
- int mMode;
- IBinder mBinder;
- WorkSource mWorkSource;
-
- DeathRecipient(int mode, String tag, IBinder binder, WorkSource ws) {
- super();
- mTag = tag;
- mMode = mode;
- mBinder = binder;
- mWorkSource = ws;
- try {
- mBinder.linkToDeath(this, 0);
- } catch (RemoteException e) {
- binderDied();
- }
- }
-
- void unlinkDeathRecipient() {
- mBinder.unlinkToDeath(this, 0);
- }
- }
-
- private class Multicaster extends DeathRecipient {
- Multicaster(String tag, IBinder binder) {
- super(Binder.getCallingUid(), tag, binder, null);
- }
-
- public void binderDied() {
- Slog.e(TAG, "Multicaster binderDied");
- synchronized (mMulticasters) {
- int i = mMulticasters.indexOf(this);
- if (i != -1) {
- removeMulticasterLocked(i, mMode);
- }
- }
- }
-
- public String toString() {
- return "Multicaster{" + mTag + " binder=" + mBinder + "}";
- }
-
- public int getUid() {
- return mMode;
- }
- }
-
- public void initializeMulticastFiltering() {
- enforceMulticastChangePermission();
-
- synchronized (mMulticasters) {
- // if anybody had requested filters be off, leave off
- if (mMulticasters.size() != 0) {
- return;
- } else {
- mWifiStateMachine.startFilteringMulticastV4Packets();
- }
- }
- }
-
- public void acquireMulticastLock(IBinder binder, String tag) {
- enforceMulticastChangePermission();
-
- synchronized (mMulticasters) {
- mMulticastEnabled++;
- mMulticasters.add(new Multicaster(tag, binder));
- // Note that we could call stopFilteringMulticastV4Packets only when
- // our new size == 1 (first call), but this function won't
- // be called often and by making the stopPacket call each
- // time we're less fragile and self-healing.
- mWifiStateMachine.stopFilteringMulticastV4Packets();
- }
-
- int uid = Binder.getCallingUid();
- final long ident = Binder.clearCallingIdentity();
- try {
- mBatteryStats.noteWifiMulticastEnabled(uid);
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- public void releaseMulticastLock() {
- enforceMulticastChangePermission();
-
- int uid = Binder.getCallingUid();
- synchronized (mMulticasters) {
- mMulticastDisabled++;
- int size = mMulticasters.size();
- for (int i = size - 1; i >= 0; i--) {
- Multicaster m = mMulticasters.get(i);
- if ((m != null) && (m.getUid() == uid)) {
- removeMulticasterLocked(i, uid);
- }
- }
- }
- }
-
- private void removeMulticasterLocked(int i, int uid)
- {
- Multicaster removed = mMulticasters.remove(i);
-
- if (removed != null) {
- removed.unlinkDeathRecipient();
- }
- if (mMulticasters.size() == 0) {
- mWifiStateMachine.startFilteringMulticastV4Packets();
- }
-
- final long ident = Binder.clearCallingIdentity();
- try {
- mBatteryStats.noteWifiMulticastDisabled(uid);
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- public boolean isMulticastEnabled() {
- enforceAccessPermission();
-
- synchronized (mMulticasters) {
- return (mMulticasters.size() > 0);
- }
- }
-}
diff --git a/services/core/java/com/android/server/wifi/WifiSettingsStore.java b/services/core/java/com/android/server/wifi/WifiSettingsStore.java
deleted file mode 100644
index 3ff8061..0000000
--- a/services/core/java/com/android/server/wifi/WifiSettingsStore.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wifi;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.provider.Settings;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/* Tracks persisted settings for Wi-Fi and airplane mode interaction */
-final class WifiSettingsStore {
- /* Values tracked in Settings.Global.WIFI_ON */
- private static final int WIFI_DISABLED = 0;
- private static final int WIFI_ENABLED = 1;
- /* Wifi enabled while in airplane mode */
- private static final int WIFI_ENABLED_AIRPLANE_OVERRIDE = 2;
- /* Wifi disabled due to airplane mode on */
- private static final int WIFI_DISABLED_AIRPLANE_ON = 3;
-
- /* Persisted state that tracks the wifi & airplane interaction from settings */
- private int mPersistWifiState = WIFI_DISABLED;
- /* Tracks current airplane mode state */
- private boolean mAirplaneModeOn = false;
-
- /* Tracks the setting of scan being available even when wi-fi is turned off
- */
- private boolean mScanAlwaysAvailable;
-
- private final Context mContext;
-
- /* Tracks if we have checked the saved wi-fi state after boot */
- private boolean mCheckSavedStateAtBoot = false;
-
- WifiSettingsStore(Context context) {
- mContext = context;
- mAirplaneModeOn = getPersistedAirplaneModeOn();
- mPersistWifiState = getPersistedWifiState();
- mScanAlwaysAvailable = getPersistedScanAlwaysAvailable();
- }
-
- synchronized boolean isWifiToggleEnabled() {
- if (!mCheckSavedStateAtBoot) {
- mCheckSavedStateAtBoot = true;
- if (testAndClearWifiSavedState()) return true;
- }
-
- if (mAirplaneModeOn) {
- return mPersistWifiState == WIFI_ENABLED_AIRPLANE_OVERRIDE;
- } else {
- return mPersistWifiState != WIFI_DISABLED;
- }
- }
-
- /**
- * Returns true if airplane mode is currently on.
- * @return {@code true} if airplane mode is on.
- */
- synchronized boolean isAirplaneModeOn() {
- return mAirplaneModeOn;
- }
-
- synchronized boolean isScanAlwaysAvailable() {
- return mScanAlwaysAvailable;
- }
-
- synchronized boolean handleWifiToggled(boolean wifiEnabled) {
- // Can Wi-Fi be toggled in airplane mode ?
- if (mAirplaneModeOn && !isAirplaneToggleable()) {
- return false;
- }
-
- if (wifiEnabled) {
- if (mAirplaneModeOn) {
- persistWifiState(WIFI_ENABLED_AIRPLANE_OVERRIDE);
- } else {
- persistWifiState(WIFI_ENABLED);
- }
- } else {
- // When wifi state is disabled, we do not care
- // if airplane mode is on or not. The scenario of
- // wifi being disabled due to airplane mode being turned on
- // is handled handleAirplaneModeToggled()
- persistWifiState(WIFI_DISABLED);
- }
- return true;
- }
-
- synchronized boolean handleAirplaneModeToggled() {
- // Is Wi-Fi sensitive to airplane mode changes ?
- if (!isAirplaneSensitive()) {
- return false;
- }
-
- mAirplaneModeOn = getPersistedAirplaneModeOn();
- if (mAirplaneModeOn) {
- // Wifi disabled due to airplane on
- if (mPersistWifiState == WIFI_ENABLED) {
- persistWifiState(WIFI_DISABLED_AIRPLANE_ON);
- }
- } else {
- /* On airplane mode disable, restore wifi state if necessary */
- if (testAndClearWifiSavedState() ||
- mPersistWifiState == WIFI_ENABLED_AIRPLANE_OVERRIDE) {
- persistWifiState(WIFI_ENABLED);
- }
- }
- return true;
- }
-
- synchronized void handleWifiScanAlwaysAvailableToggled() {
- mScanAlwaysAvailable = getPersistedScanAlwaysAvailable();
- }
-
- void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("mPersistWifiState " + mPersistWifiState);
- pw.println("mAirplaneModeOn " + mAirplaneModeOn);
- }
-
- private void persistWifiState(int state) {
- final ContentResolver cr = mContext.getContentResolver();
- mPersistWifiState = state;
- Settings.Global.putInt(cr, Settings.Global.WIFI_ON, state);
- }
-
- /* Does Wi-Fi need to be disabled when airplane mode is on ? */
- private boolean isAirplaneSensitive() {
- String airplaneModeRadios = Settings.Global.getString(mContext.getContentResolver(),
- Settings.Global.AIRPLANE_MODE_RADIOS);
- return airplaneModeRadios == null
- || airplaneModeRadios.contains(Settings.Global.RADIO_WIFI);
- }
-
- /* Is Wi-Fi allowed to be re-enabled while airplane mode is on ? */
- private boolean isAirplaneToggleable() {
- String toggleableRadios = Settings.Global.getString(mContext.getContentResolver(),
- Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
- return toggleableRadios != null
- && toggleableRadios.contains(Settings.Global.RADIO_WIFI);
- }
-
- /* After a reboot, we restore wi-fi to be on if it was turned off temporarily for tethering.
- * The settings app tracks the saved state, but the framework has to check it at boot to
- * make sure the wi-fi is turned on in case it was turned off for the purpose of tethering.
- *
- * Note that this is not part of the regular WIFI_ON setting because this only needs to
- * be controlled through the settings app and not the Wi-Fi public API.
- */
- private boolean testAndClearWifiSavedState() {
- final ContentResolver cr = mContext.getContentResolver();
- int wifiSavedState = 0;
- try {
- wifiSavedState = Settings.Global.getInt(cr, Settings.Global.WIFI_SAVED_STATE);
- if(wifiSavedState == 1)
- Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 0);
- } catch (Settings.SettingNotFoundException e) {
- ;
- }
- return (wifiSavedState == 1);
- }
-
- private int getPersistedWifiState() {
- final ContentResolver cr = mContext.getContentResolver();
- try {
- return Settings.Global.getInt(cr, Settings.Global.WIFI_ON);
- } catch (Settings.SettingNotFoundException e) {
- Settings.Global.putInt(cr, Settings.Global.WIFI_ON, WIFI_DISABLED);
- return WIFI_DISABLED;
- }
- }
-
- private boolean getPersistedAirplaneModeOn() {
- return Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
- }
-
- private boolean getPersistedScanAlwaysAvailable() {
- return Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE,
- 0) == 1;
- }
-}
diff --git a/services/core/java/com/android/server/wifi/WifiTrafficPoller.java b/services/core/java/com/android/server/wifi/WifiTrafficPoller.java
deleted file mode 100644
index b498550..0000000
--- a/services/core/java/com/android/server/wifi/WifiTrafficPoller.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wifi;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.NetworkInfo;
-import static android.net.NetworkInfo.DetailedState.CONNECTED;
-import android.net.TrafficStats;
-import android.net.wifi.WifiManager;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.util.Log;
-import android.os.Handler;
-import android.os.Message;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import com.android.internal.util.AsyncChannel;
-
-/* Polls for traffic stats and notifies the clients */
-final class WifiTrafficPoller {
- /**
- * Interval in milliseconds between polling for traffic
- * statistics
- */
- private static final int POLL_TRAFFIC_STATS_INTERVAL_MSECS = 1000;
-
- private static final int ENABLE_TRAFFIC_STATS_POLL = 1;
- private static final int TRAFFIC_STATS_POLL = 2;
- private static final int ADD_CLIENT = 3;
- private static final int REMOVE_CLIENT = 4;
-
- private boolean mEnableTrafficStatsPoll = false;
- private int mTrafficStatsPollToken = 0;
- private long mTxPkts;
- private long mRxPkts;
- /* Tracks last reported data activity */
- private int mDataActivity;
-
- private final List<Messenger> mClients = new ArrayList<Messenger>();
- // err on the side of updating at boot since screen on broadcast may be missed
- // the first time
- private AtomicBoolean mScreenOn = new AtomicBoolean(true);
- private final TrafficHandler mTrafficHandler;
- private NetworkInfo mNetworkInfo;
- private final String mInterface;
-
- WifiTrafficPoller(Context context, String iface) {
- mInterface = iface;
- mTrafficHandler = new TrafficHandler();
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(Intent.ACTION_SCREEN_ON);
-
- context.registerReceiver(
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(
- WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
- mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
- WifiManager.EXTRA_NETWORK_INFO);
- } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
- mScreenOn.set(false);
- } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
- mScreenOn.set(true);
- }
- evaluateTrafficStatsPolling();
- }
- }, filter);
- }
-
- void addClient(Messenger client) {
- Message.obtain(mTrafficHandler, ADD_CLIENT, client).sendToTarget();
- }
-
- void removeClient(Messenger client) {
- Message.obtain(mTrafficHandler, REMOVE_CLIENT, client).sendToTarget();
- }
-
-
- private class TrafficHandler extends Handler {
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case ENABLE_TRAFFIC_STATS_POLL:
- mEnableTrafficStatsPoll = (msg.arg1 == 1);
- mTrafficStatsPollToken++;
- if (mEnableTrafficStatsPoll) {
- notifyOnDataActivity();
- sendMessageDelayed(Message.obtain(this, TRAFFIC_STATS_POLL,
- mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS);
- }
- break;
- case TRAFFIC_STATS_POLL:
- if (msg.arg1 == mTrafficStatsPollToken) {
- notifyOnDataActivity();
- sendMessageDelayed(Message.obtain(this, TRAFFIC_STATS_POLL,
- mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS);
- }
- break;
- case ADD_CLIENT:
- mClients.add((Messenger) msg.obj);
- break;
- case REMOVE_CLIENT:
- mClients.remove(msg.obj);
- break;
- }
-
- }
- }
-
- private void evaluateTrafficStatsPolling() {
- Message msg;
- if (mNetworkInfo == null) return;
- if (mNetworkInfo.getDetailedState() == CONNECTED && mScreenOn.get()) {
- msg = Message.obtain(mTrafficHandler,
- ENABLE_TRAFFIC_STATS_POLL, 1, 0);
- } else {
- msg = Message.obtain(mTrafficHandler,
- ENABLE_TRAFFIC_STATS_POLL, 0, 0);
- }
- msg.sendToTarget();
- }
-
- private void notifyOnDataActivity() {
- long sent, received;
- long preTxPkts = mTxPkts, preRxPkts = mRxPkts;
- int dataActivity = WifiManager.DATA_ACTIVITY_NONE;
-
- mTxPkts = TrafficStats.getTxPackets(mInterface);
- mRxPkts = TrafficStats.getRxPackets(mInterface);
-
- if (preTxPkts > 0 || preRxPkts > 0) {
- sent = mTxPkts - preTxPkts;
- received = mRxPkts - preRxPkts;
- if (sent > 0) {
- dataActivity |= WifiManager.DATA_ACTIVITY_OUT;
- }
- if (received > 0) {
- dataActivity |= WifiManager.DATA_ACTIVITY_IN;
- }
-
- if (dataActivity != mDataActivity && mScreenOn.get()) {
- mDataActivity = dataActivity;
- for (Messenger client : mClients) {
- Message msg = Message.obtain();
- msg.what = WifiManager.DATA_ACTIVITY_NOTIFICATION;
- msg.arg1 = mDataActivity;
- try {
- client.send(msg);
- } catch (RemoteException e) {
- // Failed to reach, skip
- // Client removal is handled in WifiService
- }
- }
- }
- }
- }
-
- void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("mEnableTrafficStatsPoll " + mEnableTrafficStatsPoll);
- pw.println("mTrafficStatsPollToken " + mTrafficStatsPollToken);
- pw.println("mTxPkts " + mTxPkts);
- pw.println("mRxPkts " + mRxPkts);
- pw.println("mDataActivity " + mDataActivity);
- }
-
-}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index a737939..edc7c93 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -33,7 +33,6 @@ import android.util.Slog;
import android.view.Display;
import android.view.DragEvent;
import android.view.InputChannel;
-import android.view.Surface;
import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowManager;
diff --git a/services/core/java/com/android/server/wm/FakeWindowImpl.java b/services/core/java/com/android/server/wm/FakeWindowImpl.java
index 5a3471b..c18ea01 100644
--- a/services/core/java/com/android/server/wm/FakeWindowImpl.java
+++ b/services/core/java/com/android/server/wm/FakeWindowImpl.java
@@ -21,11 +21,9 @@ import com.android.server.input.InputWindowHandle;
import android.os.Looper;
import android.os.Process;
-import android.util.Slog;
import android.view.Display;
import android.view.InputChannel;
import android.view.InputEventReceiver;
-import android.view.InputQueue;
import android.view.WindowManagerPolicy;
public final class FakeWindowImpl implements WindowManagerPolicy.FakeWindow {
diff --git a/services/core/java/com/android/server/wm/KeyguardDisableHandler.java b/services/core/java/com/android/server/wm/KeyguardDisableHandler.java
index 859df51..c1420a8 100644
--- a/services/core/java/com/android/server/wm/KeyguardDisableHandler.java
+++ b/services/core/java/com/android/server/wm/KeyguardDisableHandler.java
@@ -24,7 +24,6 @@ import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.os.TokenWatcher;
-import android.os.UserHandle;
import android.util.Log;
import android.util.Pair;
import android.view.WindowManagerPolicy;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 85036ed..c006613 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -257,9 +257,6 @@ public class WindowManagerService extends IWindowManager.Stub
/** Amount of time (in milliseconds) to delay before declaring a window freeze timeout. */
static final int WINDOW_FREEZE_TIMEOUT_DURATION = 2000;
- /** Amount of time (in milliseconds) to delay before declaring a starting window leaked. */
- static final int STARTING_WINDOW_TIMEOUT_DURATION = 10000;
-
/**
* If true, the window manager will do its own custom freezing and general
* management of the screen during rotation.
@@ -441,8 +438,15 @@ public class WindowManagerService extends IWindowManager.Stub
int mRotation = 0;
int mForcedAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
boolean mAltOrientation = false;
- ArrayList<IRotationWatcher> mRotationWatchers
- = new ArrayList<IRotationWatcher>();
+ class RotationWatcher {
+ IRotationWatcher watcher;
+ IBinder.DeathRecipient deathRecipient;
+ RotationWatcher(IRotationWatcher w, IBinder.DeathRecipient d) {
+ watcher = w;
+ deathRecipient = d;
+ }
+ }
+ ArrayList<RotationWatcher> mRotationWatchers = new ArrayList<RotationWatcher>();
int mDeferredRotationPauseCount;
int mSystemDecorLayer = 0;
@@ -2284,8 +2288,6 @@ public class WindowManagerService extends IWindowManager.Stub
token.appWindowToken.startingWindow = win;
if (DEBUG_STARTING_WINDOW) Slog.v (TAG, "addWindow: " + token.appWindowToken
+ " startingWindow=" + win);
- Message m = mH.obtainMessage(H.REMOVE_STARTING_TIMEOUT, token.appWindowToken);
- mH.sendMessageDelayed(m, STARTING_WINDOW_TIMEOUT_DURATION);
}
boolean imMayMove = true;
@@ -2388,7 +2390,6 @@ public class WindowManagerService extends IWindowManager.Stub
public void removeWindowLocked(Session session, WindowState win) {
if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "Starting window removed " + win);
- removeStartingWindowTimeout(win.mAppToken);
}
if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && win==mCurrentFocus) Slog.v(
@@ -2533,7 +2534,6 @@ public class WindowManagerService extends IWindowManager.Stub
if (atoken != null) {
if (atoken.startingWindow == win) {
if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Nulling startingWindow " + win);
- removeStartingWindowTimeout(atoken);
atoken.startingWindow = null;
} else if (atoken.allAppWindows.size() == 0 && atoken.startingData != null) {
// If this is the last window and we had requested a starting
@@ -3881,22 +3881,22 @@ public class WindowManagerService extends IWindowManager.Stub
+ " " + mAppTransition
+ " alwaysKeepCurrent=" + alwaysKeepCurrent
+ " Callers=" + Debug.getCallers(3));
- if (okToDisplay()) {
- if (!mAppTransition.isTransitionSet() || mAppTransition.isTransitionNone()) {
+ if (!mAppTransition.isTransitionSet() || mAppTransition.isTransitionNone()) {
+ mAppTransition.setAppTransition(transit);
+ } else if (!alwaysKeepCurrent) {
+ if (transit == AppTransition.TRANSIT_TASK_OPEN
+ && mAppTransition.isTransitionEqual(
+ AppTransition.TRANSIT_TASK_CLOSE)) {
+ // Opening a new task always supersedes a close for the anim.
+ mAppTransition.setAppTransition(transit);
+ } else if (transit == AppTransition.TRANSIT_ACTIVITY_OPEN
+ && mAppTransition.isTransitionEqual(
+ AppTransition.TRANSIT_ACTIVITY_CLOSE)) {
+ // Opening a new activity always supersedes a close for the anim.
mAppTransition.setAppTransition(transit);
- } else if (!alwaysKeepCurrent) {
- if (transit == AppTransition.TRANSIT_TASK_OPEN
- && mAppTransition.isTransitionEqual(
- AppTransition.TRANSIT_TASK_CLOSE)) {
- // Opening a new task always supersedes a close for the anim.
- mAppTransition.setAppTransition(transit);
- } else if (transit == AppTransition.TRANSIT_ACTIVITY_OPEN
- && mAppTransition.isTransitionEqual(
- AppTransition.TRANSIT_ACTIVITY_CLOSE)) {
- // Opening a new activity always supersedes a close for the anim.
- mAppTransition.setAppTransition(transit);
- }
}
+ }
+ if (okToDisplay()) {
mAppTransition.prepare();
mStartingIconInTransition = false;
mSkipAppTransitionAnimation = false;
@@ -4028,7 +4028,6 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE || DEBUG_STARTING_WINDOW) {
Slog.v(TAG, "Removing starting window: " + startingWindow);
}
- removeStartingWindowTimeout(ttoken);
startingWindow.getWindowList().remove(startingWindow);
mWindowsChanged = true;
if (DEBUG_ADD_REMOVE) Slog.v(TAG,
@@ -4175,6 +4174,15 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ public void removeAppStartingWindow(IBinder token) {
+ synchronized (mWindowMap) {
+ AppWindowToken wtoken = mTokenMap.get(token).appWindowToken;
+ if (wtoken.startingWindow != null) {
+ scheduleRemoveStartingWindow(wtoken);
+ }
+ }
+ }
+
@Override
public void setAppWillBeHidden(IBinder token) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
@@ -4622,21 +4630,11 @@ public class WindowManagerService extends IWindowManager.Stub
scheduleRemoveStartingWindow(startingToken);
}
- void removeStartingWindowTimeout(AppWindowToken wtoken) {
- if (wtoken != null) {
- if (DEBUG_STARTING_WINDOW) Slog.v(TAG, Debug.getCallers(1) +
- ": Remove starting window timeout " + wtoken + (wtoken != null ?
- " startingWindow=" + wtoken.startingWindow : ""));
- mH.removeMessages(H.REMOVE_STARTING_TIMEOUT, wtoken);
- }
- }
-
void scheduleRemoveStartingWindow(AppWindowToken wtoken) {
if (wtoken != null && wtoken.startingWindow != null) {
if (DEBUG_STARTING_WINDOW) Slog.v(TAG, Debug.getCallers(1) +
": Schedule remove starting " + wtoken + (wtoken != null ?
" startingWindow=" + wtoken.startingWindow : ""));
- removeStartingWindowTimeout(wtoken);
Message m = mH.obtainMessage(H.REMOVE_STARTING, wtoken);
mH.sendMessage(m);
}
@@ -5782,7 +5780,11 @@ public class WindowManagerService extends IWindowManager.Stub
+ " surfaceLayer=" + win.mWinAnimator.mSurfaceLayer);
}
}
- rawss = SurfaceControl.screenshot(dw, dh, minLayer, maxLayer);
+ // TODO: Replace 'false' in the following line with a variable that indicates
+ // whether the screenshot should use the identity transformation matrix
+ // (e.g., enable it when taking a screenshot for recents, since we might be in
+ // the middle of the rotation animation, but don't want a rotated recent image).
+ rawss = SurfaceControl.screenshot(dw, dh, minLayer, maxLayer, false);
}
} while (!screenshotReady && retryCount <= MAX_SCREENSHOT_RETRIES);
if (retryCount > MAX_SCREENSHOT_RETRIES) Slog.i(TAG, "Screenshot max retries " +
@@ -6076,7 +6078,7 @@ public class WindowManagerService extends IWindowManager.Stub
for (int i=mRotationWatchers.size()-1; i>=0; i--) {
try {
- mRotationWatchers.get(i).onRotationChanged(rotation);
+ mRotationWatchers.get(i).watcher.onRotationChanged(rotation);
} catch (RemoteException e) {
}
}
@@ -6108,10 +6110,11 @@ public class WindowManagerService extends IWindowManager.Stub
public void binderDied() {
synchronized (mWindowMap) {
for (int i=0; i<mRotationWatchers.size(); i++) {
- if (watcherBinder == mRotationWatchers.get(i).asBinder()) {
- IRotationWatcher removed = mRotationWatchers.remove(i);
- if (removed != null) {
- removed.asBinder().unlinkToDeath(this, 0);
+ if (watcherBinder == mRotationWatchers.get(i).watcher.asBinder()) {
+ RotationWatcher removed = mRotationWatchers.remove(i);
+ IBinder binder = removed.watcher.asBinder();
+ if (binder != null) {
+ binder.unlinkToDeath(this, 0);
}
i--;
}
@@ -6123,7 +6126,7 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mWindowMap) {
try {
watcher.asBinder().linkToDeath(dr, 0);
- mRotationWatchers.add(watcher);
+ mRotationWatchers.add(new RotationWatcher(watcher, dr));
} catch (RemoteException e) {
// Client died, no cleanup needed.
}
@@ -6137,8 +6140,13 @@ public class WindowManagerService extends IWindowManager.Stub
final IBinder watcherBinder = watcher.asBinder();
synchronized (mWindowMap) {
for (int i=0; i<mRotationWatchers.size(); i++) {
- if (watcherBinder == mRotationWatchers.get(i).asBinder()) {
- mRotationWatchers.remove(i);
+ RotationWatcher rotationWatcher = mRotationWatchers.get(i);
+ if (watcherBinder == rotationWatcher.watcher.asBinder()) {
+ RotationWatcher removed = mRotationWatchers.remove(i);
+ IBinder binder = removed.watcher.asBinder();
+ if (binder != null) {
+ binder.unlinkToDeath(removed.deathRecipient, 0);
+ }
i--;
}
}
@@ -7256,7 +7264,6 @@ public class WindowManagerService extends IWindowManager.Stub
"Aborted starting " + wtoken
+ ": removed=" + wtoken.removed
+ " startingData=" + wtoken.startingData);
- removeStartingWindowTimeout(wtoken);
wtoken.startingWindow = null;
wtoken.startingData = null;
abort = true;
@@ -7281,11 +7288,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
} break;
- case REMOVE_STARTING_TIMEOUT: {
- final AppWindowToken wtoken = (AppWindowToken)msg.obj;
- Slog.e(TAG, "Starting window " + wtoken + " timed out");
- // Fall through.
- }
case REMOVE_STARTING: {
final AppWindowToken wtoken = (AppWindowToken)msg.obj;
IBinder token = null;
@@ -10235,6 +10237,12 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ public int getInputMethodWindowVisibleHeight() {
+ synchronized (mWindowMap) {
+ return mPolicy.getInputMethodWindowVisibleHeightLw();
+ }
+ }
+
@Override
public boolean hasNavigationBar() {
return mPolicy.hasNavigationBar();
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 9f3415e..a8e45c4 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -375,17 +375,19 @@ final class WindowState implements WindowManagerPolicy.WindowState {
mAttachedWindow.mChildWindows.add(this);
} else {
for (int i = 0; i < children_size; i++) {
- WindowState child = mAttachedWindow.mChildWindows.get(i);
- if (mSubLayer < child.mSubLayer) {
+ WindowState child = (WindowState)mAttachedWindow.mChildWindows.get(i);
+ if (this.mSubLayer < child.mSubLayer) {
mAttachedWindow.mChildWindows.add(i, this);
break;
- } else if (mSubLayer > child.mSubLayer) {
+ } else if (this.mSubLayer > child.mSubLayer) {
continue;
}
- if (mBaseLayer <= child.mBaseLayer) {
+ if (this.mBaseLayer <= child.mBaseLayer) {
mAttachedWindow.mChildWindows.add(i, this);
break;
+ } else {
+ continue;
}
}
if (children_size == mAttachedWindow.mChildWindows.size()) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 93f6d22..0b19b5c 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -669,10 +669,15 @@ class WindowStateAnimator {
int flags = SurfaceControl.HIDDEN;
final WindowManager.LayoutParams attrs = mWin.mAttrs;
+ final boolean isVideoPlane =
+ (attrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_VIDEO_PLANE) != 0;
if ((attrs.flags&WindowManager.LayoutParams.FLAG_SECURE) != 0) {
flags |= SurfaceControl.SECURE;
}
+ if (isVideoPlane) {
+ flags |= SurfaceControl.FX_SURFACE_VIDEO_PLANE;
+ }
if (DEBUG_VISIBILITY) Slog.v(
TAG, "Creating surface in session "
+ mSession.mSurfaceSession + " window " + this