From 7e9f4eb2608148436cef36c9969bf8a599b39e72 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Fri, 10 Sep 2010 18:43:00 -0700 Subject: [PATCH] Track client requests through location manager. This fixes a problem where applications could ask the location manager to do very heavy-weight things (like... say... update location every minute), which would get accounted against the system instead of the application because ultimately it is the system making the heavy calls (wake locks, etc). To solve this, we introduce a new class WorkSource representing the source of some work. Wake locks and Wifi locks allow you to set the source to use (but only if you are system code and thus can get the permission to do so), which is what will be reported to the battery stats until the actual caller. For the initial implementation, the location manager keeps track of all clients requesting periodic updates, and tells its providers about them as a WorkSource param when setting their min update time. The network location provider uses this to set the source on the wake and wifi locks it acquires, when doing work because of the update period. This should also be used elsewhere, such as in the GPS provider, but this is a good start. Change-Id: I2b6ffafad9e90ecf15d7c502e2db675fd52ae3cf --- api/current.xml | 160 +++++++++ .../android/commands/svc/PowerCommand.java | 2 +- core/java/android/content/SyncManager.java | 6 +- core/java/android/os/BatteryStats.java | 3 + core/java/android/os/IPowerManager.aidl | 5 +- core/java/android/os/PowerManager.java | 29 +- core/java/android/os/WorkSource.aidl | 18 + core/java/android/os/WorkSource.java | 311 ++++++++++++++++++ .../android/internal/app/IBatteryStats.aidl | 10 + .../android/internal/os/BatteryStatsImpl.java | 67 +++- core/res/AndroidManifest.xml | 2 +- .../android/location/ILocationProvider.aidl | 3 +- .../location/provider/LocationProvider.java | 18 +- .../android/server/BackupManagerService.java | 6 +- .../server/LocationManagerService.java | 27 +- .../android/server/PowerManagerService.java | 137 ++++++-- .../com/android/server/VibratorService.java | 24 +- .../java/com/android/server/WifiService.java | 101 +++++- .../server/am/ActivityManagerService.java | 18 +- .../server/am/BatteryStatsService.java | 57 ++++ .../server/location/GpsLocationProvider.java | 5 +- .../location/LocationProviderInterface.java | 3 +- .../location/LocationProviderProxy.java | 12 +- .../android/server/location/MockProvider.java | 3 +- .../server/location/PassiveProvider.java | 3 +- wifi/java/android/net/wifi/IWifiManager.aidl | 6 +- wifi/java/android/net/wifi/WifiManager.java | 30 +- 27 files changed, 971 insertions(+), 95 deletions(-) create mode 100644 core/java/android/os/WorkSource.aidl create mode 100644 core/java/android/os/WorkSource.java diff --git a/api/current.xml b/api/current.xml index d2e1f05fba2d5..d1dfc0966ce35 100644 --- a/api/current.xml +++ b/api/current.xml @@ -84326,6 +84326,8 @@ > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cmds/svc/src/com/android/commands/svc/PowerCommand.java b/cmds/svc/src/com/android/commands/svc/PowerCommand.java index d3ec3d98b357a..e1d6619916017 100644 --- a/cmds/svc/src/com/android/commands/svc/PowerCommand.java +++ b/cmds/svc/src/com/android/commands/svc/PowerCommand.java @@ -64,7 +64,7 @@ public class PowerCommand extends Svc.Command { = IPowerManager.Stub.asInterface(ServiceManager.getService(Context.POWER_SERVICE)); try { IBinder lock = new Binder(); - pm.acquireWakeLock(PowerManager.FULL_WAKE_LOCK, lock, "svc power"); + pm.acquireWakeLock(PowerManager.FULL_WAKE_LOCK, lock, "svc power", null); pm.setStayOnSetting(val); pm.releaseWakeLock(lock, 0); } diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index d0b67cca417a3..3e44fcf3a4b89 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -45,6 +45,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; +import android.os.WorkSource; import android.provider.Settings; import android.text.format.DateUtils; import android.text.format.Time; @@ -126,8 +127,8 @@ public class SyncManager implements OnAccountsUpdateListener { private static final int INITIALIZATION_UNBIND_DELAY_MS = 5000; - private static final String SYNC_WAKE_LOCK = "SyncManagerSyncWakeLock"; - private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarmWakeLock"; + private static final String SYNC_WAKE_LOCK = "*sync*"; + private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm"; private Context mContext; @@ -1695,6 +1696,7 @@ public class SyncManager implements OnAccountsUpdateListener { Log.v(TAG, "runStateIdle: setting mActiveSyncContext to " + mActiveSyncContext); } mSyncStorageEngine.setActiveSync(mActiveSyncContext); + mSyncWakeLock.setWorkSource(new WorkSource(syncAdapterInfo.uid)); if (!activeSyncContext.bindToSyncAdapter(syncAdapterInfo)) { Log.e(TAG, "Bind attempt failed to " + syncAdapterInfo); mActiveSyncContext.close(); diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index f5b1e57ca6939..f182a7ad6d714 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -404,6 +404,7 @@ public abstract class BatteryStats implements Parcelable { public static final byte CMD_UPDATE = 0; public static final byte CMD_START = 1; + public static final byte CMD_OVERFLOW = 2; public byte cmd; @@ -1703,6 +1704,8 @@ public abstract class BatteryStats implements Parcelable { pw.print(" "); if (rec.cmd == HistoryItem.CMD_START) { pw.println(" START"); + } else if (rec.cmd == HistoryItem.CMD_OVERFLOW) { + pw.println(" *OVERFLOW*"); } else { if (rec.batteryLevel < 10) pw.print("00"); else if (rec.batteryLevel < 100) pw.print("0"); diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 01cc408074d09..0067e94077772 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -17,10 +17,13 @@ package android.os; +import android.os.WorkSource; + /** @hide */ interface IPowerManager { - void acquireWakeLock(int flags, IBinder lock, String tag); + void acquireWakeLock(int flags, IBinder lock, String tag, in WorkSource ws); + void updateWakeLockWorkSource(IBinder lock, in WorkSource ws); void goToSleep(long time); void goToSleepWithReason(long time, int reason); void releaseWakeLock(IBinder lock, int flags); diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index f4ca8bc854693..3876a3e55873a 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -209,6 +209,7 @@ public class PowerManager int mCount = 0; boolean mRefCounted = true; boolean mHeld = false; + WorkSource mWorkSource; WakeLock(int flags, String tag) { @@ -247,7 +248,7 @@ public class PowerManager synchronized (mToken) { if (!mRefCounted || mCount++ == 0) { try { - mService.acquireWakeLock(mFlags, mToken, mTag); + mService.acquireWakeLock(mFlags, mToken, mTag, mWorkSource); } catch (RemoteException e) { } mHeld = true; @@ -313,6 +314,32 @@ public class PowerManager } } + public void setWorkSource(WorkSource ws) { + synchronized (mToken) { + if (ws != null && ws.size() == 0) { + ws = null; + } + boolean changed = true; + if (ws == null) { + mWorkSource = null; + } else if (mWorkSource == null) { + changed = mWorkSource != null; + mWorkSource = new WorkSource(ws); + } else { + changed = mWorkSource.diff(ws); + if (changed) { + mWorkSource.set(ws); + } + } + if (changed && mHeld) { + try { + mService.updateWakeLockWorkSource(mToken, mWorkSource); + } catch (RemoteException e) { + } + } + } + } + public String toString() { synchronized (mToken) { return "WakeLock{" diff --git a/core/java/android/os/WorkSource.aidl b/core/java/android/os/WorkSource.aidl new file mode 100644 index 0000000000000..1e7fabcbd889d --- /dev/null +++ b/core/java/android/os/WorkSource.aidl @@ -0,0 +1,18 @@ +/* Copyright 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 android.os; + +parcelable WorkSource; diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java new file mode 100644 index 0000000000000..bba1984367d73 --- /dev/null +++ b/core/java/android/os/WorkSource.java @@ -0,0 +1,311 @@ +package android.os; + +/** + * Describes the source of some work that may be done by someone else. + * Currently the public representation of what a work source is is not + * defined; this is an opaque container. + */ +public class WorkSource implements Parcelable { + int mNum; + int[] mUids; + + /** + * Internal statics to avoid object allocations in some operations. + * The WorkSource object itself is not thread safe, but we need to + * hold sTmpWorkSource lock while working with these statics. + */ + static final WorkSource sTmpWorkSource = new WorkSource(0); + /** + * For returning newbie work from a modification operation. + */ + static WorkSource sNewbWork; + /** + * For returning gone work form a modification operation. + */ + static WorkSource sGoneWork; + + /** + * Create an empty work source. + */ + public WorkSource() { + mNum = 0; + } + + /** + * Create a new WorkSource that is a copy of an existing one. + * If orig is null, an empty WorkSource is created. + */ + public WorkSource(WorkSource orig) { + if (orig == null) { + mNum = 0; + return; + } + mNum = orig.mNum; + if (orig.mUids != null) { + mUids = orig.mUids.clone(); + } else { + mUids = null; + } + } + + /** @hide */ + public WorkSource(int uid) { + mNum = 1; + mUids = new int[] { uid, 0 }; + } + + WorkSource(Parcel in) { + mNum = in.readInt(); + mUids = in.createIntArray(); + } + + /** @hide */ + public int size() { + return mNum; + } + + /** @hide */ + public int get(int index) { + return mUids[index]; + } + + /** + * Clear this WorkSource to be empty. + */ + public void clear() { + mNum = 0; + } + + /** + * Compare this WorkSource with another. + * @param other The WorkSource to compare against. + * @return If there is a difference, true is returned. + */ + public boolean diff(WorkSource other) { + int N = mNum; + if (N != other.mNum) { + return true; + } + final int[] uids1 = mUids; + final int[] uids2 = other.mUids; + for (int i=0; iother is null, the current work source + * will be made empty. + */ + public void set(WorkSource other) { + if (other == null) { + mNum = 0; + return; + } + mNum = other.mNum; + if (other.mUids != null) { + if (mUids != null && mUids.length >= mNum) { + System.arraycopy(other.mUids, 0, mUids, 0, mNum); + } else { + mUids = other.mUids.clone(); + } + } else { + mUids = null; + } + } + + /** @hide */ + public void set(int uid) { + mNum = 1; + if (mUids == null) mUids = new int[2]; + mUids[0] = uid; + } + + /** @hide */ + public WorkSource[] setReturningDiffs(WorkSource other) { + synchronized (sTmpWorkSource) { + sNewbWork = null; + sGoneWork = null; + updateLocked(other, true, true); + if (sNewbWork != null || sGoneWork != null) { + WorkSource[] diffs = new WorkSource[2]; + diffs[0] = sNewbWork; + diffs[1] = sGoneWork; + return diffs; + } + return null; + } + } + + /** + * Merge the contents of other WorkSource in to this one. + * + * @param other The other WorkSource whose contents are to be merged. + * @return Returns true if any new sources were added. + */ + public boolean add(WorkSource other) { + synchronized (sTmpWorkSource) { + return updateLocked(other, false, false); + } + } + + /** @hide */ + public WorkSource addReturningNewbs(WorkSource other) { + synchronized (sTmpWorkSource) { + sNewbWork = null; + updateLocked(other, false, true); + return sNewbWork; + } + } + + /** @hide */ + public boolean add(int uid) { + synchronized (sTmpWorkSource) { + sTmpWorkSource.mUids[0] = uid; + return updateLocked(sTmpWorkSource, false, false); + } + } + + /** @hide */ + public WorkSource addReturningNewbs(int uid) { + synchronized (sTmpWorkSource) { + sNewbWork = null; + sTmpWorkSource.mUids[0] = uid; + updateLocked(sTmpWorkSource, false, true); + return sNewbWork; + } + } + + public boolean remove(WorkSource other) { + int N1 = mNum; + final int[] uids1 = mUids; + final int N2 = other.mNum; + final int[] uids2 = other.mUids; + boolean changed = false; + int i1 = 0; + for (int i2=0; i2 uids1[i1]) { + i1++; + } + } + + mNum = N1; + + return changed; + } + + private boolean updateLocked(WorkSource other, boolean set, boolean returnNewbs) { + int N1 = mNum; + int[] uids1 = mUids; + final int N2 = other.mNum; + final int[] uids2 = other.mUids; + boolean changed = false; + int i1 = 0; + for (int i2=0; i2= N1 || uids2[i2] < uids1[i1]) { + // Need to insert a new uid. + changed = true; + if (uids1 == null) { + uids1 = new int[4]; + uids1[0] = uids2[i2]; + } else if (i1 >= uids1.length) { + int[] newuids = new int[(uids1.length*3)/2]; + if (i1 > 0) System.arraycopy(uids1, 0, newuids, 0, i1); + if (i1 < N1) System.arraycopy(uids1, i1, newuids, i1+1, N1-i1); + uids1 = newuids; + uids1[i1] = uids2[i2]; + } else { + if (i1 < N1) System.arraycopy(uids1, i1, uids1, i1+1, N1-i1); + uids1[i1] = uids2[i2]; + } + if (returnNewbs) { + if (sNewbWork == null) { + sNewbWork = new WorkSource(uids2[i2]); + } else { + sNewbWork.addLocked(uids2[i2]); + } + } + N1++; + i1++; + } else { + if (!set) { + // Skip uids that already exist or are not in 'other'. + do { + i1++; + } while (i1 < N1 && uids2[i2] >= uids1[i1]); + } else { + // Remove any uids that don't exist in 'other'. + int start = i1; + while (i1 < N1 && uids2[i2] > uids1[i1]) { + if (sGoneWork == null) { + sGoneWork = new WorkSource(uids1[i1]); + } else { + sGoneWork.addLocked(uids1[i1]); + } + i1++; + } + if (start < i1) { + System.arraycopy(uids1, i1, uids1, start, i1-start); + N1 -= i1-start; + i1 = start; + } + // If there is a matching uid, skip it. + if (i1 < N1 && uids2[i1] == uids1[i1]) { + i1++; + } + } + } + } + + mNum = N1; + mUids = uids1; + + return changed; + } + + private void addLocked(int uid) { + if (mUids == null) { + mUids = new int[4]; + mUids[0] = uid; + mNum = 1; + return; + } + if (mNum >= mUids.length) { + int[] newuids = new int[(mNum*3)/2]; + System.arraycopy(mUids, 0, newuids, 0, mNum); + mUids = newuids; + } + + mUids[mNum] = uid; + mNum++; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mNum); + dest.writeIntArray(mUids); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public WorkSource createFromParcel(Parcel in) { + return new WorkSource(in); + } + public WorkSource[] newArray(int size) { + return new WorkSource[size]; + } + }; +} diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 16207783511ce..bd87a0d05261c 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -18,6 +18,7 @@ package com.android.internal.app; import com.android.internal.os.BatteryStatsImpl; +import android.os.WorkSource; import android.telephony.SignalStrength; interface IBatteryStats { @@ -33,6 +34,9 @@ interface IBatteryStats { SensorService.cpp */ void noteStopSensor(int uid, int sensor); + void noteStartWakelockFromSource(in WorkSource ws, int pid, String name, int type); + void noteStopWakelockFromSource(in WorkSource ws, int pid, String name, int type); + void noteStartGps(int uid); void noteStopGps(int uid); void noteScreenOn(); @@ -57,6 +61,12 @@ interface IBatteryStats { void noteScanWifiLockReleased(int uid); void noteWifiMulticastEnabled(int uid); void noteWifiMulticastDisabled(int uid); + void noteFullWifiLockAcquiredFromSource(in WorkSource ws); + void noteFullWifiLockReleasedFromSource(in WorkSource ws); + void noteScanWifiLockAcquiredFromSource(in WorkSource ws); + void noteScanWifiLockReleasedFromSource(in WorkSource ws); + void noteWifiMulticastEnabledFromSource(in WorkSource ws); + void noteWifiMulticastDisabledFromSource(in WorkSource ws); void setBatteryState(int status, int health, int plugType, int level, int temp, int volt); long getAwakeTimeBattery(); long getAwakeTimePlugged(); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 0ef1ea578eb7d..753dbf0e41416 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -29,6 +29,7 @@ import android.os.ParcelFormatException; import android.os.Parcelable; import android.os.Process; import android.os.SystemClock; +import android.os.WorkSource; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.TelephonyManager; @@ -68,12 +69,12 @@ public final class BatteryStatsImpl extends BatteryStats { private static final int VERSION = 50; // Maximum number of items we will record in the history. - private static final int MAX_HISTORY_ITEMS = 1000; + private static final int MAX_HISTORY_ITEMS = 2000; // The maximum number of names wakelocks we will keep track of // per uid; once the limit is reached, we batch the remaining wakelocks // in to one common name. - private static final int MAX_WAKELOCKS_PER_UID = 20; + private static final int MAX_WAKELOCKS_PER_UID = 30; private static final String BATCHED_WAKELOCK_NAME = "*overflow*"; @@ -1171,7 +1172,7 @@ public final class BatteryStatsImpl extends BatteryStats { // If the current time is basically the same as the last time, // just collapse into one record. if (mHistoryEnd != null && mHistoryEnd.cmd == HistoryItem.CMD_UPDATE - && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+100)) { + && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+500)) { // If the current is the same as the one before, then we no // longer need the entry. if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE @@ -1187,6 +1188,10 @@ public final class BatteryStatsImpl extends BatteryStats { return; } + if (mNumHistoryItems == MAX_HISTORY_ITEMS) { + addHistoryRecordLocked(curTime, HistoryItem.CMD_OVERFLOW); + } + if (mNumHistoryItems >= MAX_HISTORY_ITEMS) { // Once we've reached the maximum number of items, we only // record changes to the battery level. @@ -1327,6 +1332,20 @@ public final class BatteryStatsImpl extends BatteryStats { } } + public void noteStartWakeFromSourceLocked(WorkSource ws, int pid, String name, int type) { + int N = ws.size(); + for (int i=0; i + android:protectionLevel="signatureOrSystem" /> diff --git a/location/java/android/location/ILocationProvider.aidl b/location/java/android/location/ILocationProvider.aidl index 2b9782a64ddb2..ecf678914bb74 100644 --- a/location/java/android/location/ILocationProvider.aidl +++ b/location/java/android/location/ILocationProvider.aidl @@ -20,6 +20,7 @@ import android.location.Criteria; import android.location.Location; import android.net.NetworkInfo; import android.os.Bundle; +import android.os.WorkSource; /** * Binder interface for services that implement location providers. @@ -43,7 +44,7 @@ interface ILocationProvider { long getStatusUpdateTime(); String getInternalState(); void enableLocationTracking(boolean enable); - void setMinTime(long minTime); + void setMinTime(long minTime, in WorkSource ws); void updateNetworkState(int state, in NetworkInfo info); void updateLocation(in Location location); boolean sendExtraCommand(String command, inout Bundle extras); diff --git a/location/java/android/location/provider/LocationProvider.java b/location/java/android/location/provider/LocationProvider.java index cf939de50d614..95b4425bfc25f 100644 --- a/location/java/android/location/provider/LocationProvider.java +++ b/location/java/android/location/provider/LocationProvider.java @@ -26,6 +26,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.WorkSource; import android.util.Log; /** @@ -106,8 +107,8 @@ public abstract class LocationProvider { LocationProvider.this.onEnableLocationTracking(enable); } - public void setMinTime(long minTime) { - LocationProvider.this.onSetMinTime(minTime); + public void setMinTime(long minTime, WorkSource ws) { + LocationProvider.this.onSetMinTime(minTime, ws); } public void updateNetworkState(int state, NetworkInfo info) { @@ -123,11 +124,11 @@ public abstract class LocationProvider { } public void addListener(int uid) { - LocationProvider.this.onAddListener(uid); + LocationProvider.this.onAddListener(uid, new WorkSource(uid)); } public void removeListener(int uid) { - LocationProvider.this.onRemoveListener(uid); + LocationProvider.this.onRemoveListener(uid, new WorkSource(uid)); } }; @@ -303,8 +304,9 @@ public abstract class LocationProvider { * the frequency of updates to match the requested frequency. * * @param minTime the smallest minTime value over all listeners for this provider. + * @param ws the source this work is coming from. */ - public abstract void onSetMinTime(long minTime); + public abstract void onSetMinTime(long minTime, WorkSource ws); /** * Updates the network state for the given provider. This function must @@ -340,14 +342,16 @@ public abstract class LocationProvider { * Notifies the location provider when a new client is listening for locations. * * @param uid user ID of the new client. + * @param ws a WorkSource representation of the client. */ - public abstract void onAddListener(int uid); + public abstract void onAddListener(int uid, WorkSource ws); /** * Notifies the location provider when a client is no longer listening for locations. * * @param uid user ID of the client no longer listening. + * @param ws a WorkSource representation of the client. */ - public abstract void onRemoveListener(int uid); + public abstract void onRemoveListener(int uid, WorkSource ws); } diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index e454c0867b310..aa87f29314800 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -54,6 +54,7 @@ import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; +import android.os.WorkSource; import android.provider.Settings; import android.util.EventLog; import android.util.Slog; @@ -504,7 +505,7 @@ class BackupManagerService extends IBackupManager.Stub { parseLeftoverJournals(); // Power management - mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "backup"); + mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*"); // Start the backup passes going setBackupEnabled(areEnabled); @@ -1363,6 +1364,7 @@ class BackupManagerService extends IBackupManager.Stub { ? IApplicationThread.BACKUP_MODE_FULL : IApplicationThread.BACKUP_MODE_INCREMENTAL; try { + mWakelock.setWorkSource(new WorkSource(request.appInfo.uid)); agent = bindToAgentSynchronous(request.appInfo, mode); if (agent != null) { int result = processOneBackup(request, agent, transport); @@ -1378,6 +1380,8 @@ class BackupManagerService extends IBackupManager.Stub { } } + mWakelock.setWorkSource(null); + return BackupConstants.TRANSPORT_OK; } diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index a38970f7ebf49..f3ce8baaf7856 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -52,6 +52,7 @@ import android.os.Message; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; +import android.os.WorkSource; import android.provider.Settings; import android.util.Log; import android.util.Slog; @@ -157,6 +158,12 @@ public class LocationManagerService extends ILocationManager.Stub implements Run private final HashMap> mRecordsByProvider = new HashMap>(); + /** + * Temporary filled in when computing min time for a provider. Access is + * protected by global lock mLock. + */ + private final WorkSource mTmpWorkSource = new WorkSource(); + // Proximity listeners private Receiver mProximityReceiver = null; private ILocationListener mProximityListener = null; @@ -912,7 +919,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run if (enabled) { p.enable(); if (listeners > 0) { - p.setMinTime(getMinTimeLocked(provider)); + p.setMinTime(getMinTimeLocked(provider), mTmpWorkSource); p.enableLocationTracking(true); } } else { @@ -924,9 +931,21 @@ public class LocationManagerService extends ILocationManager.Stub implements Run private long getMinTimeLocked(String provider) { long minTime = Long.MAX_VALUE; ArrayList records = mRecordsByProvider.get(provider); + mTmpWorkSource.clear(); if (records != null) { for (int i=records.size()-1; i>=0; i--) { - minTime = Math.min(minTime, records.get(i).mMinTime); + UpdateRecord ur = records.get(i); + long curTime = ur.mMinTime; + if (curTime < minTime) { + minTime = curTime; + } + } + long inclTime = (minTime*3)/2; + for (int i=records.size()-1; i>=0; i--) { + UpdateRecord ur = records.get(i); + if (ur.mMinTime <= inclTime) { + mTmpWorkSource.add(ur.mUid); + } } } return minTime; @@ -1124,7 +1143,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run boolean isProviderEnabled = isAllowedBySettingsLocked(provider); if (isProviderEnabled) { long minTimeForProvider = getMinTimeLocked(provider); - p.setMinTime(minTimeForProvider); + p.setMinTime(minTimeForProvider, mTmpWorkSource); // try requesting single shot if singleShot is true, and fall back to // regular location tracking if requestSingleShotFix() is not supported if (!singleShot || !p.requestSingleShotFix()) { @@ -1222,7 +1241,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run LocationProviderInterface p = mProvidersByName.get(provider); if (p != null) { if (hasOtherListener) { - p.setMinTime(getMinTimeLocked(provider)); + p.setMinTime(getMinTimeLocked(provider), mTmpWorkSource); } else { p.enableLocationTracking(false); } diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index 16f3f1071e769..af8d7d42d85d3 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -50,6 +50,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; +import android.os.WorkSource; import android.provider.Settings.SettingNotFoundException; import android.provider.Settings; import android.util.EventLog; @@ -310,7 +311,7 @@ class PowerManagerService extends IPowerManager.Stub long ident = Binder.clearCallingIdentity(); try { PowerManagerService.this.acquireWakeLockLocked(mFlags, mToken, - MY_UID, MY_PID, mTag); + MY_UID, MY_PID, mTag, null); mHeld = true; } finally { Binder.restoreCallingIdentity(ident); @@ -607,6 +608,7 @@ class PowerManagerService extends IPowerManager.Stub final int uid; final int pid; final int monitorType; + WorkSource ws; boolean activated = true; int minState; } @@ -630,35 +632,74 @@ class PowerManagerService extends IPowerManager.Stub || n == PowerManager.SCREEN_DIM_WAKE_LOCK; } - public void acquireWakeLock(int flags, IBinder lock, String tag) { + void enforceWakeSourcePermission(int uid, int pid) { + if (uid == Process.myUid()) { + return; + } + mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS, + pid, uid, null); + } + + public void acquireWakeLock(int flags, IBinder lock, String tag, WorkSource ws) { int uid = Binder.getCallingUid(); int pid = Binder.getCallingPid(); if (uid != Process.myUid()) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); } + if (ws != null) { + enforceWakeSourcePermission(uid, pid); + } long ident = Binder.clearCallingIdentity(); try { synchronized (mLocks) { - acquireWakeLockLocked(flags, lock, uid, pid, tag); + acquireWakeLockLocked(flags, lock, uid, pid, tag, ws); } } finally { Binder.restoreCallingIdentity(ident); } } - public void acquireWakeLockLocked(int flags, IBinder lock, int uid, int pid, String tag) { - int acquireUid = -1; - int acquirePid = -1; - String acquireName = null; - int acquireType = -1; + void noteStartWakeLocked(WakeLock wl, WorkSource ws) { + try { + if (ws != null) { + mBatteryStats.noteStartWakelockFromSource(ws, wl.pid, wl.tag, + wl.monitorType); + } else { + mBatteryStats.noteStartWakelock(wl.uid, wl.pid, wl.tag, wl.monitorType); + } + } catch (RemoteException e) { + // Ignore + } + } + void noteStopWakeLocked(WakeLock wl, WorkSource ws) { + try { + if (ws != null) { + mBatteryStats.noteStopWakelockFromSource(ws, wl.pid, wl.tag, + wl.monitorType); + } else { + mBatteryStats.noteStopWakelock(wl.uid, wl.pid, wl.tag, wl.monitorType); + } + } catch (RemoteException e) { + // Ignore + } + } + + public void acquireWakeLockLocked(int flags, IBinder lock, int uid, int pid, String tag, + WorkSource ws) { if (mSpew) { Slog.d(TAG, "acquireWakeLock flags=0x" + Integer.toHexString(flags) + " tag=" + tag); } + if (ws != null && ws.size() == 0) { + ws = null; + } + int index = mLocks.getIndex(lock); WakeLock wl; boolean newlock; + boolean diffsource; + WorkSource oldsource; if (index < 0) { wl = new WakeLock(flags, lock, tag, uid, pid); switch (wl.flags & LOCK_MASK) @@ -687,10 +728,31 @@ class PowerManagerService extends IPowerManager.Stub return; } mLocks.addLock(wl); + if (ws != null) { + wl.ws = new WorkSource(ws); + } newlock = true; + diffsource = false; + oldsource = null; } else { wl = mLocks.get(index); newlock = false; + oldsource = wl.ws; + if (oldsource != null) { + if (ws == null) { + wl.ws = null; + diffsource = true; + } else { + diffsource = oldsource.diff(ws); + } + } else if (ws != null) { + diffsource = true; + } else { + diffsource = false; + } + if (diffsource) { + wl.ws = new WorkSource(ws); + } } if (isScreenLock(flags)) { // if this causes a wakeup, we reactivate all of the locks and @@ -731,19 +793,41 @@ class PowerManagerService extends IPowerManager.Stub enableProximityLockLocked(); } } - if (newlock) { - acquireUid = wl.uid; - acquirePid = wl.pid; - acquireName = wl.tag; - acquireType = wl.monitorType; - } - if (acquireType >= 0) { - try { - mBatteryStats.noteStartWakelock(acquireUid, acquirePid, acquireName, acquireType); - } catch (RemoteException e) { - // Ignore + if (diffsource) { + // If the lock sources have changed, need to first release the + // old ones. + noteStopWakeLocked(wl, oldsource); + } + if (newlock || diffsource) { + noteStartWakeLocked(wl, ws); + } + } + + public void updateWakeLockWorkSource(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.getIndex(lock); + if (index < 0) { + throw new IllegalArgumentException("Wake lock not active"); + } + WakeLock wl = mLocks.get(index); + WorkSource oldsource = wl.ws; + wl.ws = ws != null ? new WorkSource(ws) : null; + noteStopWakeLocked(wl, oldsource); + noteStartWakeLocked(wl, ws); } + } finally { + Binder.restoreCallingIdentity(ident); } } @@ -759,11 +843,6 @@ class PowerManagerService extends IPowerManager.Stub } private void releaseWakeLockLocked(IBinder lock, int flags, boolean death) { - int releaseUid; - int releasePid; - String releaseName; - int releaseType; - WakeLock wl = mLocks.removeLock(lock); if (wl == null) { return; @@ -804,17 +883,11 @@ class PowerManagerService extends IPowerManager.Stub } // Unlink the lock from the binder. wl.binder.unlinkToDeath(wl, 0); - releaseUid = wl.uid; - releasePid = wl.pid; - releaseName = wl.tag; - releaseType = wl.monitorType; - if (releaseType >= 0) { + if (wl.monitorType >= 0) { long origId = Binder.clearCallingIdentity(); try { - mBatteryStats.noteStopWakelock(releaseUid, releasePid, releaseName, releaseType); - } catch (RemoteException e) { - // Ignore + noteStopWakeLocked(wl, wl.ws); } finally { Binder.restoreCallingIdentity(origId); } diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java index 2e7e3e18dcddd..f0b59555b2b9e 100755 --- a/services/java/com/android/server/VibratorService.java +++ b/services/java/com/android/server/VibratorService.java @@ -29,6 +29,7 @@ import android.os.RemoteException; import android.os.IBinder; import android.os.Binder; import android.os.SystemClock; +import android.os.WorkSource; import android.util.Slog; import java.util.LinkedList; @@ -39,6 +40,7 @@ public class VibratorService extends IVibratorService.Stub { private final LinkedList mVibrations; private Vibration mCurrentVibration; + private final WorkSource mTmpWorkSource = new WorkSource(); private class Vibration implements IBinder.DeathRecipient { private final IBinder mToken; @@ -46,22 +48,24 @@ public class VibratorService extends IVibratorService.Stub { private final long mStartTime; private final long[] mPattern; private final int mRepeat; + private final int mUid; - Vibration(IBinder token, long millis) { - this(token, millis, null, 0); + Vibration(IBinder token, long millis, int uid) { + this(token, millis, null, 0, uid); } - Vibration(IBinder token, long[] pattern, int repeat) { - this(token, 0, pattern, repeat); + Vibration(IBinder token, long[] pattern, int repeat, int uid) { + this(token, 0, pattern, repeat, uid); } private Vibration(IBinder token, long millis, long[] pattern, - int repeat) { + int repeat, int uid) { mToken = token; mTimeout = millis; mStartTime = SystemClock.uptimeMillis(); mPattern = pattern; mRepeat = repeat; + mUid = uid; } public void binderDied() { @@ -98,7 +102,7 @@ public class VibratorService extends IVibratorService.Stub { mContext = context; PowerManager pm = (PowerManager)context.getSystemService( Context.POWER_SERVICE); - mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*"); mWakeLock.setReferenceCounted(true); mVibrations = new LinkedList(); @@ -113,6 +117,7 @@ public class VibratorService extends IVibratorService.Stub { != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires VIBRATE permission"); } + int uid = Binder.getCallingUid(); // We're running in the system server so we cannot crash. Check for a // timeout of 0 or negative. This will ensure that a vibration has // either a timeout of > 0 or a non-null pattern. @@ -122,7 +127,7 @@ public class VibratorService extends IVibratorService.Stub { // longer than milliseconds. return; } - Vibration vib = new Vibration(token, milliseconds); + Vibration vib = new Vibration(token, milliseconds, uid); synchronized (mVibrations) { removeVibrationLocked(token); doCancelVibrateLocked(); @@ -146,6 +151,7 @@ public class VibratorService extends IVibratorService.Stub { != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires VIBRATE permission"); } + int uid = Binder.getCallingUid(); // so wakelock calls will succeed long identity = Binder.clearCallingIdentity(); try { @@ -165,7 +171,7 @@ public class VibratorService extends IVibratorService.Stub { return; } - Vibration vib = new Vibration(token, pattern, repeat); + Vibration vib = new Vibration(token, pattern, repeat, uid); try { token.linkToDeath(vib, 0); } catch (RemoteException e) { @@ -280,6 +286,8 @@ public class VibratorService extends IVibratorService.Stub { VibrateThread(Vibration vib) { mVibration = vib; + mTmpWorkSource.set(vib.mUid); + mWakeLock.setWorkSource(mTmpWorkSource); mWakeLock.acquire(); } diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 0eca0825bca6a..f11c0f74c0f2f 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -63,6 +63,7 @@ import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.WorkSource; import android.provider.Settings; import android.util.Slog; import android.text.TextUtils; @@ -2036,8 +2037,8 @@ public class WifiService extends IWifiManager.Stub { } private class WifiLock extends DeathRecipient { - WifiLock(int lockMode, String tag, IBinder binder) { - super(lockMode, tag, binder); + WifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) { + super(lockMode, tag, binder, ws); } public void binderDied() { @@ -2111,7 +2112,15 @@ public class WifiService extends IWifiManager.Stub { } } - public boolean acquireWifiLock(IBinder binder, int lockMode, String tag) { + void enforceWakeSourcePermission(int uid, int pid) { + if (uid == 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 && @@ -2120,34 +2129,68 @@ public class WifiService extends IWifiManager.Stub { if (DBG) throw new IllegalArgumentException("lockMode=" + lockMode); return false; } - WifiLock wifiLock = new WifiLock(lockMode, tag, binder); + if (ws != null) { + enforceWakeSourcePermission(Binder.getCallingUid(), Binder.getCallingPid()); + } + if (ws != null && ws.size() == 0) { + ws = null; + } + 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: + mBatteryStats.noteFullWifiLockAcquiredFromSource(wifiLock.mWorkSource); + break; + case WifiManager.WIFI_MODE_FULL_HIGH_PERF: + /* Treat high power as a full lock for battery stats */ + mBatteryStats.noteFullWifiLockAcquiredFromSource(wifiLock.mWorkSource); + break; + case WifiManager.WIFI_MODE_SCAN_ONLY: + mBatteryStats.noteScanWifiLockAcquiredFromSource(wifiLock.mWorkSource); + break; + } + } + + private void noteReleaseWifiLock(WifiLock wifiLock) throws RemoteException { + switch(wifiLock.mMode) { + case WifiManager.WIFI_MODE_FULL: + mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource); + break; + case WifiManager.WIFI_MODE_FULL_HIGH_PERF: + /* Treat high power as a full lock for battery stats */ + mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource); + break; + case WifiManager.WIFI_MODE_SCAN_ONLY: + mBatteryStats.noteScanWifiLockReleasedFromSource(wifiLock.mWorkSource); + break; + } + } + private boolean acquireWifiLockLocked(WifiLock wifiLock) { Slog.d(TAG, "acquireWifiLockLocked: " + wifiLock); mLocks.addLock(wifiLock); - int uid = Binder.getCallingUid(); long ident = Binder.clearCallingIdentity(); try { + noteAcquireWifiLock(wifiLock); switch(wifiLock.mMode) { case WifiManager.WIFI_MODE_FULL: ++mFullLocksAcquired; - mBatteryStats.noteFullWifiLockAcquired(uid); break; case WifiManager.WIFI_MODE_FULL_HIGH_PERF: ++mFullHighPerfLocksAcquired; - /* Treat high power as a full lock for battery stats */ - mBatteryStats.noteFullWifiLockAcquired(uid); break; - case WifiManager.WIFI_MODE_SCAN_ONLY: ++mScanLocksAcquired; - mBatteryStats.noteScanWifiLockAcquired(uid); break; } } catch (RemoteException e) { @@ -2159,6 +2202,33 @@ public class WifiService extends IWifiManager.Stub { return true; } + 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) { @@ -2176,21 +2246,18 @@ public class WifiService extends IWifiManager.Stub { hadLock = (wifiLock != null); if (hadLock) { - int uid = Binder.getCallingUid(); long ident = Binder.clearCallingIdentity(); try { + noteAcquireWifiLock(wifiLock); switch(wifiLock.mMode) { case WifiManager.WIFI_MODE_FULL: ++mFullLocksReleased; - mBatteryStats.noteFullWifiLockReleased(uid); break; case WifiManager.WIFI_MODE_FULL_HIGH_PERF: ++mFullHighPerfLocksReleased; - mBatteryStats.noteFullWifiLockReleased(uid); break; case WifiManager.WIFI_MODE_SCAN_ONLY: ++mScanLocksReleased; - mBatteryStats.noteScanWifiLockReleased(uid); break; } } catch (RemoteException e) { @@ -2208,12 +2275,14 @@ public class WifiService extends IWifiManager.Stub { String mTag; int mMode; IBinder mBinder; + WorkSource mWorkSource; - DeathRecipient(int mode, String tag, IBinder binder) { + 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) { @@ -2228,7 +2297,7 @@ public class WifiService extends IWifiManager.Stub { private class Multicaster extends DeathRecipient { Multicaster(String tag, IBinder binder) { - super(Binder.getCallingUid(), tag, binder); + super(Binder.getCallingUid(), tag, binder, null); } public void binderDied() { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 2cdf31e957910..cf767ca878cf9 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -5985,12 +5985,18 @@ public final class ActivityManagerService extends ActivityManagerNative finisher = new IIntentReceiver.Stub() { public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, - boolean sticky) - throws RemoteException { - synchronized (ActivityManagerService.this) { - mDidUpdate = true; - } - systemReady(goingCallback); + boolean sticky) { + // The raw IIntentReceiver interface is called + // with the AM lock held, so redispatch to + // execute our code without the lock. + mHandler.post(new Runnable() { + public void run() { + synchronized (ActivityManagerService.this) { + mDidUpdate = true; + } + systemReady(goingCallback); + } + }); } }; } diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java index 7314e041cdcd8..bb40967005b6a 100644 --- a/services/java/com/android/server/am/BatteryStatsService.java +++ b/services/java/com/android/server/am/BatteryStatsService.java @@ -23,6 +23,7 @@ import android.os.IBinder; import android.os.Parcel; import android.os.Process; import android.os.ServiceManager; +import android.os.WorkSource; import android.telephony.SignalStrength; import android.util.Slog; @@ -107,6 +108,20 @@ public final class BatteryStatsService extends IBatteryStats.Stub { } } + public void noteStartWakelockFromSource(WorkSource ws, int pid, String name, int type) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteStartWakeFromSourceLocked(ws, pid, name, type); + } + } + + public void noteStopWakelockFromSource(WorkSource ws, int pid, String name, int type) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteStopWakeFromSourceLocked(ws, pid, name, type); + } + } + public void noteStartSensor(int uid, int sensor) { enforceCallingPermission(); synchronized (mStats) { @@ -317,6 +332,48 @@ public final class BatteryStatsService extends IBatteryStats.Stub { } } + public void noteFullWifiLockAcquiredFromSource(WorkSource ws) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteFullWifiLockAcquiredFromSourceLocked(ws); + } + } + + public void noteFullWifiLockReleasedFromSource(WorkSource ws) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteFullWifiLockReleasedFromSourceLocked(ws); + } + } + + public void noteScanWifiLockAcquiredFromSource(WorkSource ws) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteScanWifiLockAcquiredFromSourceLocked(ws); + } + } + + public void noteScanWifiLockReleasedFromSource(WorkSource ws) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteScanWifiLockReleasedFromSourceLocked(ws); + } + } + + public void noteWifiMulticastEnabledFromSource(WorkSource ws) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteWifiMulticastEnabledFromSourceLocked(ws); + } + } + + public void noteWifiMulticastDisabledFromSource(WorkSource ws) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteWifiMulticastDisabledFromSourceLocked(ws); + } + } + public boolean isOnBattery() { return mStats.isOnBattery(); } diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java index c1165c7bc7f36..3bf6ee4062f4a 100755 --- a/services/java/com/android/server/location/GpsLocationProvider.java +++ b/services/java/com/android/server/location/GpsLocationProvider.java @@ -44,6 +44,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; +import android.os.WorkSource; import android.provider.Settings; import android.util.Log; import android.util.SparseIntArray; @@ -736,7 +737,7 @@ public class GpsLocationProvider implements LocationProviderInterface { startNavigating(true); } - public void setMinTime(long minTime) { + public void setMinTime(long minTime, WorkSource ws) { if (DEBUG) Log.d(TAG, "setMinTime " + minTime); if (minTime >= 0) { @@ -779,7 +780,7 @@ public class GpsLocationProvider implements LocationProviderInterface { public void addListener(int uid) { synchronized (mWakeLock) { mPendingListenerMessages++; - mWakeLock.acquire(); + mWakeLock.acquire(); Message m = Message.obtain(mHandler, ADD_LISTENER); m.arg1 = uid; mHandler.sendMessage(m); diff --git a/services/java/com/android/server/location/LocationProviderInterface.java b/services/java/com/android/server/location/LocationProviderInterface.java index 084ab81f48d24..858a582c24fac 100644 --- a/services/java/com/android/server/location/LocationProviderInterface.java +++ b/services/java/com/android/server/location/LocationProviderInterface.java @@ -20,6 +20,7 @@ import android.location.Criteria; import android.location.Location; import android.net.NetworkInfo; import android.os.Bundle; +import android.os.WorkSource; /** * Location Manager's interface for location providers. @@ -47,7 +48,7 @@ public interface LocationProviderInterface { /* returns false if single shot is not supported */ boolean requestSingleShotFix(); String getInternalState(); - void setMinTime(long minTime); + void setMinTime(long minTime, WorkSource ws); void updateNetworkState(int state, NetworkInfo info); void updateLocation(Location location); boolean sendExtraCommand(String command, Bundle extras); diff --git a/services/java/com/android/server/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java index 24d7737010dbd..7dc9920779483 100644 --- a/services/java/com/android/server/location/LocationProviderProxy.java +++ b/services/java/com/android/server/location/LocationProviderProxy.java @@ -29,6 +29,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; +import android.os.WorkSource; import android.util.Log; import com.android.internal.location.DummyLocationProvider; @@ -52,6 +53,7 @@ public class LocationProviderProxy implements LocationProviderInterface { private boolean mLocationTracking = false; private boolean mEnabled = false; private long mMinTime = -1; + private WorkSource mMinTimeSource = new WorkSource(); private int mNetworkState; private NetworkInfo mNetworkInfo; @@ -122,7 +124,7 @@ public class LocationProviderProxy implements LocationProviderInterface { provider.enableLocationTracking(true); } if (mMinTime >= 0) { - provider.setMinTime(mMinTime); + provider.setMinTime(mMinTime, mMinTimeSource); } if (mNetworkInfo != null) { provider.updateNetworkState(mNetworkState, mNetworkInfo); @@ -318,6 +320,7 @@ public class LocationProviderProxy implements LocationProviderInterface { mLocationTracking = enable; if (!enable) { mMinTime = -1; + mMinTimeSource.clear(); } ILocationProvider provider; synchronized (mServiceConnection) { @@ -339,15 +342,16 @@ public class LocationProviderProxy implements LocationProviderInterface { return mMinTime; } - public void setMinTime(long minTime) { - mMinTime = minTime; + public void setMinTime(long minTime, WorkSource ws) { + mMinTime = minTime; + mMinTimeSource.set(ws); ILocationProvider provider; synchronized (mServiceConnection) { provider = mProvider; } if (provider != null) { try { - provider.setMinTime(minTime); + provider.setMinTime(minTime, ws); } catch (RemoteException e) { } } diff --git a/services/java/com/android/server/location/MockProvider.java b/services/java/com/android/server/location/MockProvider.java index 01b34b744ffa2..09d799ff9ee0d 100644 --- a/services/java/com/android/server/location/MockProvider.java +++ b/services/java/com/android/server/location/MockProvider.java @@ -23,6 +23,7 @@ import android.location.LocationProvider; import android.net.NetworkInfo; import android.os.Bundle; import android.os.RemoteException; +import android.os.WorkSource; import android.util.Log; import android.util.PrintWriterPrinter; @@ -201,7 +202,7 @@ public class MockProvider implements LocationProviderInterface { return false; } - public void setMinTime(long minTime) { + public void setMinTime(long minTime, WorkSource ws) { } public void updateNetworkState(int state, NetworkInfo info) { diff --git a/services/java/com/android/server/location/PassiveProvider.java b/services/java/com/android/server/location/PassiveProvider.java index 7fc93f8b635ca..ea0d1b0219df4 100644 --- a/services/java/com/android/server/location/PassiveProvider.java +++ b/services/java/com/android/server/location/PassiveProvider.java @@ -24,6 +24,7 @@ import android.location.LocationProvider; import android.net.NetworkInfo; import android.os.Bundle; import android.os.RemoteException; +import android.os.WorkSource; import android.util.Log; /** @@ -123,7 +124,7 @@ public class PassiveProvider implements LocationProviderInterface { return false; } - public void setMinTime(long minTime) { + public void setMinTime(long minTime, WorkSource ws) { } public void updateNetworkState(int state, NetworkInfo info) { diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 6e0bc9d04f3ff..0ee559e7c2287 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -21,6 +21,8 @@ import android.net.wifi.WifiConfiguration; import android.net.wifi.ScanResult; import android.net.DhcpInfo; +import android.os.WorkSource; + /** * Interface that allows controlling and querying Wi-Fi connectivity. * @@ -66,7 +68,9 @@ interface IWifiManager DhcpInfo getDhcpInfo(); - boolean acquireWifiLock(IBinder lock, int lockType, String tag); + boolean acquireWifiLock(IBinder lock, int lockType, String tag, in WorkSource ws); + + void updateWifiLockWorkSource(IBinder lock, in WorkSource ws); boolean releaseWifiLock(IBinder lock); diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 9d2152181e39e..dd162f212cbd5 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -23,6 +23,7 @@ import android.os.Binder; import android.os.IBinder; import android.os.Handler; import android.os.RemoteException; +import android.os.WorkSource; import java.util.List; @@ -872,6 +873,7 @@ public class WifiManager { int mLockType; private boolean mRefCounted; private boolean mHeld; + private WorkSource mWorkSource; private WifiLock(int lockType, String tag) { mTag = tag; @@ -897,7 +899,7 @@ public class WifiManager { synchronized (mBinder) { if (mRefCounted ? (++mRefCount > 0) : (!mHeld)) { try { - mService.acquireWifiLock(mBinder, mLockType, mTag); + mService.acquireWifiLock(mBinder, mLockType, mTag, mWorkSource); synchronized (WifiManager.this) { if (mActiveLockCount >= MAX_ACTIVE_LOCKS) { mService.releaseWifiLock(mBinder); @@ -969,6 +971,32 @@ public class WifiManager { } } + public void setWorkSource(WorkSource ws) { + synchronized (mBinder) { + if (ws != null && ws.size() == 0) { + ws = null; + } + boolean changed = true; + if (ws == null) { + mWorkSource = null; + } else if (mWorkSource == null) { + changed = mWorkSource != null; + mWorkSource = new WorkSource(ws); + } else { + changed = mWorkSource.diff(ws); + if (changed) { + mWorkSource.set(ws); + } + } + if (changed && mHeld) { + try { + mService.updateWifiLockWorkSource(mBinder, mWorkSource); + } catch (RemoteException e) { + } + } + } + } + public String toString() { String s1, s2, s3; synchronized (mBinder) {