field public static final int ratingBarStyleIndicator = 16843280; // 0x1010210
field public static final int ratingBarStyleSmall = 16842877; // 0x101007d
field public static final int readPermission = 16842759; // 0x1010007
+ field public static final int relinquishTaskIdentity = 16843893; // 0x1010475
field public static final int repeatCount = 16843199; // 0x10101bf
field public static final int repeatMode = 16843200; // 0x10101c0
field public static final int reqFiveWayNav = 16843314; // 0x1010232
field public static final int FLAG_IMMERSIVE = 2048; // 0x800
field public static final int FLAG_MULTIPROCESS = 1; // 0x1
field public static final int FLAG_NO_HISTORY = 128; // 0x80
+ field public static final int FLAG_RELINQUISH_TASK_IDENTITY = 4096; // 0x1000
field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
field public static final int FLAG_STATE_NOT_NEEDED = 16; // 0x10
field public static final int LAUNCH_MULTIPLE = 0; // 0x0
field public static final java.lang.String CONTENT_DIRECTORY = "data";
}
- public static final class ContactsContract.Contacts.Entity implements android.provider.BaseColumns android.provider.ContactsContract.BaseSyncColumns android.provider.ContactsContract.ContactNameColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.ContactStatusColumns android.provider.ContactsContract.ContactsColumns android.provider.ContactsContract.DataColumns android.provider.ContactsContract.RawContactsColumns android.provider.ContactsContract.StatusColumns android.provider.ContactsContract.SyncColumns {
+ public static final class ContactsContract.Contacts.Entity implements android.provider.BaseColumns android.provider.ContactsContract.BaseSyncColumns android.provider.ContactsContract.ContactNameColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.ContactStatusColumns android.provider.ContactsContract.ContactsColumns android.provider.ContactsContract.DataColumns android.provider.ContactsContract.DataUsageStatColumns android.provider.ContactsContract.RawContactsColumns android.provider.ContactsContract.StatusColumns android.provider.ContactsContract.SyncColumns {
field public static final java.lang.String CONTENT_DIRECTORY = "entities";
field public static final java.lang.String DATA_ID = "data_id";
field public static final java.lang.String RAW_CONTACT_ID = "raw_contact_id";
* @hide
*/
public class BackupTransport {
+ // Zero return always means things are okay. If returned from
+ // getNextFullRestoreDataChunk(), it means that no data could be delivered at
+ // this time, but the restore is still running and the caller should simply
+ // retry.
public static final int TRANSPORT_OK = 0;
- public static final int TRANSPORT_ERROR = 1;
- public static final int TRANSPORT_NOT_INITIALIZED = 2;
- public static final int TRANSPORT_PACKAGE_REJECTED = 3;
- public static final int AGENT_ERROR = 4;
- public static final int AGENT_UNKNOWN = 5;
+
+ // -1 is special; it is used in getNextFullRestoreDataChunk() to indicate that
+ // we've delivered the entire data stream for the current restore target.
+ public static final int NO_MORE_DATA = -1;
+
+ // Result codes that indicate real errors are negative and not -1
+ public static final int TRANSPORT_ERROR = -1000;
+ public static final int TRANSPORT_NOT_INITIALIZED = -1001;
+ public static final int TRANSPORT_PACKAGE_REJECTED = -1002;
+ public static final int AGENT_ERROR = -1003;
+ public static final int AGENT_UNKNOWN = -1004;
IBackupTransport mBinderImpl = new TransportImpl();
/** @hide */
* @param socket The file descriptor that the transport will use for delivering the
* streamed archive. The transport must close this socket in all cases when returning
* from this method.
- * @return 0 when no more data for the current package is available. A positive value
- * indicates the presence of that many bytes to be delivered to the app. Any negative
- * return value is treated as equivalent to {@link BackupTransport#TRANSPORT_ERROR},
- * indicating a fatal error condition that precludes further restore operations
- * on the current dataset.
+ * @return {@link #NO_MORE_DATA} when no more data for the current package is available.
+ * A positive value indicates the presence of that many bytes to be delivered to the app.
+ * A value of zero indicates that no data was deliverable at this time, but the restore
+ * is still running and the caller should retry. {@link #TRANSPORT_PACKAGE_REJECTED}
+ * means that the current package's restore operation should be aborted, but that
+ * the transport itself is still in a good state and so a multiple-package restore
+ * sequence can still be continued. Any other negative return value is treated as a
+ * fatal error condition that aborts all further restore operations on the current dataset.
*/
public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) {
return 0;
*/
public static final int FLAG_IMMERSIVE = 0x0800;
/**
+ * Bit in {@link #flags}: If set, a task rooted at this activity will have its
+ * baseIntent replaced by the activity immediately above this. Each activity may further
+ * relinquish its identity to the activity above it using this flag. Set from the
+ * android.R.attr#relinquishTaskIdentity attribute.
+ */
+ public static final int FLAG_RELINQUISH_TASK_IDENTITY = 0x1000;
+ /**
* Bit in {@link #flags} indicating that tasks started with this activity are to be
* removed from the recent list of tasks when the last activity in the task is finished.
* {@link android.R.attr#autoRemoveFromRecents}
PackageParser.Package pkg = parser.parseMonolithicPackage(apkFile, 0);
if ((flags & GET_SIGNATURES) != 0) {
parser.collectCertificates(pkg, 0);
+ parser.collectManifestDigest(pkg);
}
PackageUserState state = new PackageUserState();
return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state);
* {@code AndroidManifest.xml}, {@code true} is returned.
*/
public void collectManifestDigest(Package pkg) throws PackageParserException {
+ pkg.manifestDigest = null;
+
// TODO: extend to gather digest for split APKs
try {
final StrictJarFile jarFile = new StrictJarFile(pkg.codePath);
false)) {
a.info.flags |= ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS;
}
+
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestActivity_relinquishTaskIdentity,
+ false)) {
+ a.info.flags |= ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
+ }
} else {
a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
a.info.configChanges = 0;
private static final int NO_ERROR = 0;
private static final int EACCESS = -13;
private static final int ENODEV = -19;
+ private static final int EBUSY = -16;
+ private static final int EINVAL = -22;
+ private static final int ENOSYS = -38;
+ private static final int EUSERS = -87;
+ private static final int EOPNOTSUPP = -95;
/**
* Broadcast Action: A new picture is taken by the camera, and the entry of
public static final String ACTION_NEW_VIDEO = "android.hardware.action.NEW_VIDEO";
/**
+ * Camera HAL device API version 1.0
+ * @hide
+ */
+ public static final int CAMERA_HAL_API_VERSION_1_0 = 0x100;
+
+ /**
+ * A constant meaning the normal camera connect/open will be used.
+ * @hide
+ */
+ public static final int CAMERA_HAL_API_VERSION_NORMAL_OPEN = -2;
+
+ /**
+ * Used to indicate HAL version un-specified.
+ */
+ private static final int CAMERA_HAL_API_VERSION_UNSPECIFIED = -1;
+ /**
* Hardware face detection. It does not use much CPU.
*/
private static final int CAMERA_FACE_DETECTION_HW = 0;
return null;
}
+ /**
+ * Creates a new Camera object to access a particular hardware camera with
+ * given hal API version. If the same camera is opened by other applications
+ * or the hal API version is not supported by this device, this will throw a
+ * RuntimeException.
+ * <p>
+ * You must call {@link #release()} when you are done using the camera,
+ * otherwise it will remain locked and be unavailable to other applications.
+ * <p>
+ * Your application should only have one Camera object active at a time for
+ * a particular hardware camera.
+ * <p>
+ * Callbacks from other methods are delivered to the event loop of the
+ * thread which called open(). If this thread has no event loop, then
+ * callbacks are delivered to the main application event loop. If there is
+ * no main application event loop, callbacks are not delivered.
+ * <p class="caution">
+ * <b>Caution:</b> On some devices, this method may take a long time to
+ * complete. It is best to call this method from a worker thread (possibly
+ * using {@link android.os.AsyncTask}) to avoid blocking the main
+ * application UI thread.
+ *
+ * @param cameraId The hardware camera to access, between 0 and
+ * {@link #getNumberOfCameras()}-1.
+ * @param halVersion The HAL API version this camera device to be opened as. When
+ * it is {@value #CAMERA_HAL_API_VERSION_NORMAL_OPEN}, the methods will be equivalent
+ * to {@link #open}, but more detailed error information will be returned to managed code.
+ * @return a new Camera object, connected, locked and ready for use.
+ * @throws RuntimeException if opening the camera fails (for example, if the
+ * camera is in use by another process or device policy manager has disabled
+ * the camera).
+ * @see android.app.admin.DevicePolicyManager#getCameraDisabled(android.content.ComponentName)
+ *
+ * @hide
+ */
+ public static Camera openLegacy(int cameraId, int halVersion) {
+ return new Camera(cameraId, halVersion);
+ }
+
+ /**
+ * Create a legacy camera object.
+ *
+ * @param cameraId The hardware camera to access, between 0 and
+ * {@link #getNumberOfCameras()}-1.
+ * @param halVersion The HAL API version this camera device to be opened as.
+ */
+ private Camera(int cameraId, int halVersion) {
+ int err = cameraInit(cameraId, halVersion);
+ if (checkInitErrors(err)) {
+ switch(err) {
+ case EACCESS:
+ throw new RuntimeException("Fail to connect to camera service");
+ case ENODEV:
+ throw new RuntimeException("Camera initialization failed");
+ case ENOSYS:
+ throw new RuntimeException("Camera initialization failed because some methods"
+ + " are not implemented");
+ case EOPNOTSUPP:
+ throw new RuntimeException("Camera initialization failed because the hal"
+ + " version is not supported by this device");
+ case EINVAL:
+ throw new RuntimeException("Camera initialization failed because the input"
+ + " arugments are invalid");
+ case EBUSY:
+ throw new RuntimeException("Camera initialization failed because the camera"
+ + " device was already opened");
+ case EUSERS:
+ throw new RuntimeException("Camera initialization failed because the max"
+ + " number of camera devices were already opened");
+ default:
+ // Should never hit this.
+ throw new RuntimeException("Unknown camera error");
+ }
+ }
+ }
+
+ private int cameraInit(int cameraId, int halVersion) {
+ // This function should be only called by Camera(int cameraId, int halVersion).
+ if (halVersion < CAMERA_HAL_API_VERSION_1_0 &&
+ halVersion != CAMERA_HAL_API_VERSION_NORMAL_OPEN) {
+ throw new IllegalArgumentException("Invalid HAL version " + halVersion);
+ }
+
+ mShutterCallback = null;
+ mRawImageCallback = null;
+ mJpegCallback = null;
+ mPreviewCallback = null;
+ mPostviewCallback = null;
+ mUsingPreviewAllocation = false;
+ mZoomListener = null;
+
+ Looper looper;
+ if ((looper = Looper.myLooper()) != null) {
+ mEventHandler = new EventHandler(this, looper);
+ } else if ((looper = Looper.getMainLooper()) != null) {
+ mEventHandler = new EventHandler(this, looper);
+ } else {
+ mEventHandler = null;
+ }
+
+ String packageName = ActivityThread.currentPackageName();
+
+ return native_setup(new WeakReference<Camera>(this), cameraId, halVersion, packageName);
+ }
+
Camera(int cameraId) {
int err = cameraInit(cameraId);
if (checkInitErrors(err)) {
String packageName = ActivityThread.currentPackageName();
- return native_setup(new WeakReference<Camera>(this), cameraId, packageName);
+ return native_setup(new WeakReference<Camera>(this), cameraId,
+ CAMERA_HAL_API_VERSION_UNSPECIFIED, packageName);
}
/**
release();
}
- private native final int native_setup(Object camera_this, int cameraId,
+ private native final int native_setup(Object camera_this, int cameraId, int halVersion,
String packageName);
private native final void native_release();
int getLegacyParameters(int cameraId, out String[] parameters);
// Determines if a particular API version is supported; see ICameraService.h for version defines
int supportsCameraApi(int cameraId, int apiVersion);
+
+ int connectLegacy(ICameraClient client, int cameraId,
+ int halVersion,
+ String clientPackageName,
+ int clientUid,
+ // Container for an ICamera object
+ out BinderHolder device);
}
*/
public static final class Entity implements BaseColumns, ContactsColumns,
ContactNameColumns, RawContactsColumns, BaseSyncColumns, SyncColumns, DataColumns,
- StatusColumns, ContactOptionsColumns, ContactStatusColumns {
+ StatusColumns, ContactOptionsColumns, ContactStatusColumns, DataUsageStatColumns {
/**
* no public constructor since this is a utility class
*/
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
-import android.webkit.WebViewFactory;
import com.android.internal.util.GrowingArrayUtils;
// that is done.
public static long COMMIT_PERIOD = 3*60*60*1000; // Commit current stats every 3 hours
+ // Minimum uptime period before committing. If the COMMIT_PERIOD has elapsed but
+ // the total uptime has not exceeded this amount, then the commit will be held until
+ // it is reached.
+ public static long COMMIT_UPTIME_PERIOD = 60*60*1000; // Must have at least 1 hour elapsed
+
public static final int STATE_NOTHING = -1;
public static final int STATE_PERSISTENT = 0;
public static final int STATE_TOP = 1;
public static final int PSS_USS_MAXIMUM = 6;
public static final int PSS_COUNT = PSS_USS_MAXIMUM+1;
+ public static final int SYS_MEM_USAGE_SAMPLE_COUNT = 0;
+ public static final int SYS_MEM_USAGE_CACHED_MINIMUM = 1;
+ public static final int SYS_MEM_USAGE_CACHED_AVERAGE = 2;
+ public static final int SYS_MEM_USAGE_CACHED_MAXIMUM = 3;
+ public static final int SYS_MEM_USAGE_FREE_MINIMUM = 4;
+ public static final int SYS_MEM_USAGE_FREE_AVERAGE = 5;
+ public static final int SYS_MEM_USAGE_FREE_MAXIMUM = 6;
+ public static final int SYS_MEM_USAGE_ZRAM_MINIMUM = 7;
+ public static final int SYS_MEM_USAGE_ZRAM_AVERAGE = 8;
+ public static final int SYS_MEM_USAGE_ZRAM_MAXIMUM = 9;
+ public static final int SYS_MEM_USAGE_KERNEL_MINIMUM = 10;
+ public static final int SYS_MEM_USAGE_KERNEL_AVERAGE = 11;
+ public static final int SYS_MEM_USAGE_KERNEL_MAXIMUM = 12;
+ public static final int SYS_MEM_USAGE_NATIVE_MINIMUM = 13;
+ public static final int SYS_MEM_USAGE_NATIVE_AVERAGE = 14;
+ public static final int SYS_MEM_USAGE_NATIVE_MAXIMUM = 15;
+ public static final int SYS_MEM_USAGE_COUNT = SYS_MEM_USAGE_NATIVE_MAXIMUM+1;
+
public static final int ADJ_NOTHING = -1;
public static final int ADJ_MEM_FACTOR_NORMAL = 0;
public static final int ADJ_MEM_FACTOR_MODERATE = 1;
static final String CSV_SEP = "\t";
// Current version of the parcel format.
- private static final int PARCEL_VERSION = 14;
+ private static final int PARCEL_VERSION = 18;
// In-memory Parcel magic number, used to detect attempts to unmarshall bad data
private static final int MAGIC = 0x50535453;
public int mMemFactor = STATE_NOTHING;
public long mStartTime;
+ public int[] mSysMemUsageTable = null;
+ public int mSysMemUsageTableSize = 0;
+ public final long[] mSysMemUsageArgs = new long[SYS_MEM_USAGE_COUNT];
+
public long mTimePeriodStartClock;
public long mTimePeriodStartRealtime;
public long mTimePeriodEndRealtime;
+ public long mTimePeriodStartUptime;
+ public long mTimePeriodEndUptime;
String mRuntime;
- String mWebView;
boolean mRunning;
static final int LONGS_SIZE = 4096;
mMemFactorDurations[i] += other.mMemFactorDurations[i];
}
+ for (int i=0; i<other.mSysMemUsageTableSize; i++) {
+ int ent = other.mSysMemUsageTable[i];
+ int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+ long[] longs = other.mLongs.get((ent>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+ addSysMemUsage(state, longs, ((ent >> OFFSET_INDEX_SHIFT) & OFFSET_INDEX_MASK));
+ }
+
if (other.mTimePeriodStartClock < mTimePeriodStartClock) {
mTimePeriodStartClock = other.mTimePeriodStartClock;
mTimePeriodStartClockStr = other.mTimePeriodStartClockStr;
}
mTimePeriodEndRealtime += other.mTimePeriodEndRealtime - other.mTimePeriodStartRealtime;
+ mTimePeriodEndUptime += other.mTimePeriodEndUptime - other.mTimePeriodStartUptime;
+ }
+
+ public void addSysMemUsage(long cachedMem, long freeMem, long zramMem, long kernelMem,
+ long nativeMem) {
+ if (mMemFactor != STATE_NOTHING) {
+ int state = mMemFactor * STATE_COUNT;
+ mSysMemUsageArgs[SYS_MEM_USAGE_SAMPLE_COUNT] = 1;
+ for (int i=0; i<3; i++) {
+ mSysMemUsageArgs[SYS_MEM_USAGE_CACHED_MINIMUM + i] = cachedMem;
+ mSysMemUsageArgs[SYS_MEM_USAGE_FREE_MINIMUM + i] = freeMem;
+ mSysMemUsageArgs[SYS_MEM_USAGE_ZRAM_MINIMUM + i] = zramMem;
+ mSysMemUsageArgs[SYS_MEM_USAGE_KERNEL_MINIMUM + i] = kernelMem;
+ mSysMemUsageArgs[SYS_MEM_USAGE_NATIVE_MINIMUM + i] = nativeMem;
+ }
+ addSysMemUsage(state, mSysMemUsageArgs, 0);
+ }
+ }
+
+ void addSysMemUsage(int state, long[] data, int dataOff) {
+ int idx = binarySearch(mSysMemUsageTable, mSysMemUsageTableSize, state);
+ int off;
+ if (idx >= 0) {
+ off = mSysMemUsageTable[idx];
+ } else {
+ mAddLongTable = mSysMemUsageTable;
+ mAddLongTableSize = mSysMemUsageTableSize;
+ off = addLongData(~idx, state, SYS_MEM_USAGE_COUNT);
+ mSysMemUsageTable = mAddLongTable;
+ mSysMemUsageTableSize = mAddLongTableSize;
+ }
+ long[] longs = mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+ idx = (off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK;
+ addSysMemUsage(longs, idx, data, dataOff);
+ }
+
+ static void addSysMemUsage(long[] dstData, int dstOff, long[] addData, int addOff) {
+ final long dstCount = dstData[dstOff+SYS_MEM_USAGE_SAMPLE_COUNT];
+ final long addCount = addData[addOff+SYS_MEM_USAGE_SAMPLE_COUNT];
+ if (dstCount == 0) {
+ dstData[dstOff+SYS_MEM_USAGE_SAMPLE_COUNT] = addCount;
+ for (int i=SYS_MEM_USAGE_CACHED_MINIMUM; i<SYS_MEM_USAGE_COUNT; i++) {
+ dstData[dstOff+i] = addData[addOff+i];
+ }
+ } else if (addCount > 0) {
+ dstData[dstOff+SYS_MEM_USAGE_SAMPLE_COUNT] = dstCount + addCount;
+ for (int i=SYS_MEM_USAGE_CACHED_MINIMUM; i<SYS_MEM_USAGE_COUNT; i+=3) {
+ if (dstData[dstOff+i] > addData[addOff+i]) {
+ dstData[dstOff+i] = addData[addOff+i];
+ }
+ dstData[dstOff+i+1] = (long)(
+ ((dstData[dstOff+i+1]*(double)dstCount)
+ + (addData[addOff+i+1]*(double)addCount))
+ / (dstCount+addCount) );
+ if (dstData[dstOff+i+2] < addData[addOff+i+2]) {
+ dstData[dstOff+i+2] = addData[addOff+i+2];
+ }
+ }
+ }
}
public static final Parcelable.Creator<ProcessStats> CREATOR
return totalTime;
}
+ static class PssAggr {
+ long pss = 0;
+ long samples = 0;
+
+ void add(long newPss, long newSamples) {
+ pss = (long)( (pss*(double)samples) + (newPss*(double)newSamples) )
+ / (samples+newSamples);
+ samples += newSamples;
+ }
+ }
+
+ public void computeTotalMemoryUse(TotalMemoryUseCollection data, long now) {
+ data.totalTime = 0;
+ for (int i=0; i<STATE_COUNT; i++) {
+ data.processStateWeight[i] = 0;
+ data.processStatePss[i] = 0;
+ data.processStateTime[i] = 0;
+ data.processStateSamples[i] = 0;
+ }
+ for (int i=0; i<SYS_MEM_USAGE_COUNT; i++) {
+ data.sysMemUsage[i] = 0;
+ }
+ data.sysMemCachedWeight = 0;
+ data.sysMemFreeWeight = 0;
+ data.sysMemZRamWeight = 0;
+ data.sysMemKernelWeight = 0;
+ data.sysMemNativeWeight = 0;
+ data.sysMemSamples = 0;
+ long[] totalMemUsage = new long[SYS_MEM_USAGE_COUNT];
+ for (int i=0; i<mSysMemUsageTableSize; i++) {
+ int ent = mSysMemUsageTable[i];
+ long[] longs = mLongs.get((ent>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+ int idx = (ent >> OFFSET_INDEX_SHIFT) & OFFSET_INDEX_MASK;
+ addSysMemUsage(totalMemUsage, 0, longs, idx);
+ }
+ for (int is=0; is<data.screenStates.length; is++) {
+ for (int im=0; im<data.memStates.length; im++) {
+ int memBucket = data.screenStates[is] + data.memStates[im];
+ int stateBucket = memBucket * STATE_COUNT;
+ long memTime = mMemFactorDurations[memBucket];
+ if (mMemFactor == memBucket) {
+ memTime += now - mStartTime;
+ }
+ data.totalTime += memTime;
+ int sysIdx = binarySearch(mSysMemUsageTable, mSysMemUsageTableSize, stateBucket);
+ long[] longs = totalMemUsage;
+ int idx = 0;
+ if (sysIdx >= 0) {
+ int ent = mSysMemUsageTable[sysIdx];
+ long[] tmpLongs = mLongs.get((ent>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+ int tmpIdx = (ent >> OFFSET_INDEX_SHIFT) & OFFSET_INDEX_MASK;
+ if (tmpLongs[tmpIdx+SYS_MEM_USAGE_SAMPLE_COUNT] >= 3) {
+ addSysMemUsage(data.sysMemUsage, 0, longs, idx);
+ longs = tmpLongs;
+ idx = tmpIdx;
+ }
+ }
+ data.sysMemCachedWeight += longs[idx+SYS_MEM_USAGE_CACHED_AVERAGE]
+ * (double)memTime;
+ data.sysMemFreeWeight += longs[idx+SYS_MEM_USAGE_FREE_AVERAGE]
+ * (double)memTime;
+ data.sysMemZRamWeight += longs[idx+SYS_MEM_USAGE_ZRAM_AVERAGE]
+ * (double)memTime;
+ data.sysMemKernelWeight += longs[idx+SYS_MEM_USAGE_KERNEL_AVERAGE]
+ * (double)memTime;
+ data.sysMemNativeWeight += longs[idx+SYS_MEM_USAGE_NATIVE_AVERAGE]
+ * (double)memTime;
+ data.sysMemSamples += longs[idx+SYS_MEM_USAGE_SAMPLE_COUNT];
+ }
+ }
+ ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+ for (int iproc=0; iproc<procMap.size(); iproc++) {
+ SparseArray<ProcessState> uids = procMap.valueAt(iproc);
+ for (int iu=0; iu<uids.size(); iu++) {
+ final ProcessState proc = uids.valueAt(iu);
+ final PssAggr fgPss = new PssAggr();
+ final PssAggr bgPss = new PssAggr();
+ final PssAggr cachedPss = new PssAggr();
+ boolean havePss = false;
+ for (int i=0; i<proc.mDurationsTableSize; i++) {
+ int off = proc.mDurationsTable[i];
+ int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+ int procState = type % STATE_COUNT;
+ long samples = proc.getPssSampleCount(type);
+ if (samples > 0) {
+ long avg = proc.getPssAverage(type);
+ havePss = true;
+ if (procState <= STATE_IMPORTANT_FOREGROUND) {
+ fgPss.add(avg, samples);
+ } else if (procState <= STATE_RECEIVER) {
+ bgPss.add(avg, samples);
+ } else {
+ cachedPss.add(avg, samples);
+ }
+ }
+ }
+ if (!havePss) {
+ continue;
+ }
+ boolean fgHasBg = false;
+ boolean fgHasCached = false;
+ boolean bgHasCached = false;
+ if (fgPss.samples < 3 && bgPss.samples > 0) {
+ fgHasBg = true;
+ fgPss.add(bgPss.pss, bgPss.samples);
+ }
+ if (fgPss.samples < 3 && cachedPss.samples > 0) {
+ fgHasCached = true;
+ fgPss.add(cachedPss.pss, cachedPss.samples);
+ }
+ if (bgPss.samples < 3 && cachedPss.samples > 0) {
+ bgHasCached = true;
+ bgPss.add(cachedPss.pss, cachedPss.samples);
+ }
+ if (bgPss.samples < 3 && !fgHasBg && fgPss.samples > 0) {
+ bgPss.add(fgPss.pss, fgPss.samples);
+ }
+ if (cachedPss.samples < 3 && !bgHasCached && bgPss.samples > 0) {
+ cachedPss.add(bgPss.pss, bgPss.samples);
+ }
+ if (cachedPss.samples < 3 && !fgHasCached && fgPss.samples > 0) {
+ cachedPss.add(fgPss.pss, fgPss.samples);
+ }
+ for (int i=0; i<proc.mDurationsTableSize; i++) {
+ final int off = proc.mDurationsTable[i];
+ final int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+ long time = getLong(off, 0);
+ if (proc.mCurState == type) {
+ time += now - proc.mStartTime;
+ }
+ final int procState = type % STATE_COUNT;
+ data.processStateTime[procState] += time;
+ long samples = proc.getPssSampleCount(type);
+ long avg;
+ if (samples > 0) {
+ avg = proc.getPssAverage(type);
+ } else if (procState <= STATE_IMPORTANT_FOREGROUND) {
+ samples = fgPss.samples;
+ avg = fgPss.pss;
+ } else if (procState <= STATE_RECEIVER) {
+ samples = bgPss.samples;
+ avg = bgPss.pss;
+ } else {
+ samples = cachedPss.samples;
+ avg = cachedPss.pss;
+ }
+ double newAvg = ( (data.processStatePss[procState]
+ * (double)data.processStateSamples[procState])
+ + (avg*(double)samples)
+ ) / (data.processStateSamples[procState]+samples);
+ data.processStatePss[procState] = (long)newAvg;
+ data.processStateSamples[procState] += samples;
+ data.processStateWeight[procState] += avg * (double)time;
+ }
+ }
+ }
+ }
+
static void dumpProcessState(PrintWriter pw, String prefix, ProcessState proc,
int[] screenStates, int[] memStates, int[] procStates, long now) {
long totalTime = 0;
}
}
+ long getSysMemUsageValue(int state, int index) {
+ int idx = binarySearch(mSysMemUsageTable, mSysMemUsageTableSize, state);
+ return idx >= 0 ? getLong(mSysMemUsageTable[idx], index) : 0;
+ }
+
+ void dumpSysMemUsageCategory(PrintWriter pw, String prefix, String label,
+ int bucket, int index) {
+ pw.print(prefix); pw.print(label);
+ pw.print(": ");
+ printSizeValue(pw, getSysMemUsageValue(bucket, index) * 1024);
+ pw.print(" min, ");
+ printSizeValue(pw, getSysMemUsageValue(bucket, index + 1) * 1024);
+ pw.print(" avg, ");
+ printSizeValue(pw, getSysMemUsageValue(bucket, index+2) * 1024);
+ pw.println(" max");
+ }
+
+ void dumpSysMemUsage(PrintWriter pw, String prefix, int[] screenStates,
+ int[] memStates) {
+ int printedScreen = -1;
+ for (int is=0; is<screenStates.length; is++) {
+ int printedMem = -1;
+ for (int im=0; im<memStates.length; im++) {
+ final int iscreen = screenStates[is];
+ final int imem = memStates[im];
+ final int bucket = ((iscreen + imem) * STATE_COUNT);
+ long count = getSysMemUsageValue(bucket, SYS_MEM_USAGE_SAMPLE_COUNT);
+ if (count > 0) {
+ pw.print(prefix);
+ if (screenStates.length > 1) {
+ printScreenLabel(pw, printedScreen != iscreen
+ ? iscreen : STATE_NOTHING);
+ printedScreen = iscreen;
+ }
+ if (memStates.length > 1) {
+ printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING, '\0');
+ printedMem = imem;
+ }
+ pw.print(": ");
+ pw.print(count);
+ pw.println(" samples:");
+ dumpSysMemUsageCategory(pw, prefix, " Cached", bucket,
+ SYS_MEM_USAGE_CACHED_MINIMUM);
+ dumpSysMemUsageCategory(pw, prefix, " Free", bucket,
+ SYS_MEM_USAGE_FREE_MINIMUM);
+ dumpSysMemUsageCategory(pw, prefix, " ZRam", bucket,
+ SYS_MEM_USAGE_ZRAM_MINIMUM);
+ dumpSysMemUsageCategory(pw, prefix, " Kernel", bucket,
+ SYS_MEM_USAGE_KERNEL_MINIMUM);
+ dumpSysMemUsageCategory(pw, prefix, " Native", bucket,
+ SYS_MEM_USAGE_NATIVE_MINIMUM);
+ }
+ }
+ }
+ }
+
static void dumpStateHeadersCsv(PrintWriter pw, String sep, int[] screenStates,
int[] memStates, int[] procStates) {
final int NS = screenStates != null ? screenStates.length : 1;
mTimePeriodStartClock = System.currentTimeMillis();
buildTimePeriodStartClockStr();
mTimePeriodStartRealtime = mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
+ mTimePeriodStartUptime = mTimePeriodEndUptime = SystemClock.uptimeMillis();
mLongs.clear();
mLongs.add(new long[LONGS_SIZE]);
mNextLong = 0;
Arrays.fill(mMemFactorDurations, 0);
+ mSysMemUsageTable = null;
+ mSysMemUsageTableSize = 0;
mStartTime = 0;
mReadError = null;
mFlags = 0;
@Override
public void writeToParcel(Parcel out, int flags) {
- long now = SystemClock.uptimeMillis();
+ writeToParcel(out, SystemClock.uptimeMillis(), flags);
+ }
+
+ /** @hide */
+ public void writeToParcel(Parcel out, long now, int flags) {
out.writeInt(MAGIC);
out.writeInt(PARCEL_VERSION);
out.writeInt(STATE_COUNT);
out.writeInt(ADJ_COUNT);
out.writeInt(PSS_COUNT);
+ out.writeInt(SYS_MEM_USAGE_COUNT);
out.writeInt(LONGS_SIZE);
mCommonStringToIndex = new ArrayMap<String, Integer>(mProcesses.mMap.size());
out.writeLong(mTimePeriodStartClock);
out.writeLong(mTimePeriodStartRealtime);
out.writeLong(mTimePeriodEndRealtime);
+ out.writeLong(mTimePeriodStartUptime);
+ out.writeLong(mTimePeriodEndUptime);
out.writeString(mRuntime);
- out.writeString(mWebView);
out.writeInt(mFlags);
out.writeInt(mLongs.size());
}
writeCompactedLongArray(out, mMemFactorDurations, mMemFactorDurations.length);
+ out.writeInt(mSysMemUsageTableSize);
+ for (int i=0; i<mSysMemUsageTableSize; i++) {
+ if (DEBUG_PARCEL) Slog.i(TAG, "Writing sys mem usage #" + i + ": "
+ + printLongOffset(mSysMemUsageTable[i]));
+ out.writeInt(mSysMemUsageTable[i]);
+ }
+
out.writeInt(NPROC);
for (int ip=0; ip<NPROC; ip++) {
writeCommonString(out, procMap.keyAt(ip));
if (!readCheckedInt(in, PSS_COUNT, "pss count")) {
return;
}
+ if (!readCheckedInt(in, SYS_MEM_USAGE_COUNT, "sys mem usage count")) {
+ return;
+ }
if (!readCheckedInt(in, LONGS_SIZE, "longs size")) {
return;
}
buildTimePeriodStartClockStr();
mTimePeriodStartRealtime = in.readLong();
mTimePeriodEndRealtime = in.readLong();
+ mTimePeriodStartUptime = in.readLong();
+ mTimePeriodEndUptime = in.readLong();
mRuntime = in.readString();
- mWebView = in.readString();
mFlags = in.readInt();
final int NLONGS = in.readInt();
readCompactedLongArray(in, version, mMemFactorDurations, mMemFactorDurations.length);
+ mSysMemUsageTable = readTableFromParcel(in, TAG, "sys mem usage");
+ if (mSysMemUsageTable == BAD_TABLE) {
+ return;
+ }
+ mSysMemUsageTableSize = mSysMemUsageTable != null ? mSysMemUsageTable.length : 0;
+
int NPROC = in.readInt();
if (NPROC < 0) {
mReadError = "bad process count: " + NPROC;
boolean dumpAll, boolean activeOnly) {
long totalTime = dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
mStartTime, now);
+ if (mSysMemUsageTable != null) {
+ pw.println("System memory usage:");
+ dumpSysMemUsage(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ);
+ }
ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
boolean printedHeader = false;
boolean sepNeeded = false;
dumpTotalsLocked(pw, now);
}
+ long printMemoryCategory(PrintWriter pw, String prefix, String label, double memWeight,
+ long totalTime, long curTotalMem, int samples) {
+ if (memWeight != 0) {
+ long mem = (long)(memWeight * 1024 / totalTime);
+ pw.print(prefix);
+ pw.print(label);
+ pw.print(": ");
+ printSizeValue(pw, mem);
+ pw.print(" (");
+ pw.print(samples);
+ pw.print(" samples)");
+ pw.println();
+ return curTotalMem + mem;
+ }
+ return curTotalMem;
+ }
+
void dumpTotalsLocked(PrintWriter pw, long now) {
pw.println("Run time Stats:");
dumpSingleTime(pw, " ", mMemFactorDurations, mMemFactor, mStartTime, now);
pw.println();
+ pw.println("Memory usage:");
+ TotalMemoryUseCollection totalMem = new TotalMemoryUseCollection(ALL_SCREEN_ADJ,
+ ALL_MEM_ADJ);
+ computeTotalMemoryUse(totalMem, now);
+ long totalPss = 0;
+ totalPss = printMemoryCategory(pw, " ", "Kernel ", totalMem.sysMemKernelWeight,
+ totalMem.totalTime, totalPss, totalMem.sysMemSamples);
+ totalPss = printMemoryCategory(pw, " ", "Native ", totalMem.sysMemNativeWeight,
+ totalMem.totalTime, totalPss, totalMem.sysMemSamples);
+ for (int i=0; i<STATE_COUNT; i++) {
+ // Skip restarting service state -- that is not actually a running process.
+ if (i != STATE_SERVICE_RESTARTING) {
+ totalPss = printMemoryCategory(pw, " ", STATE_NAMES[i],
+ totalMem.processStateWeight[i], totalMem.totalTime, totalPss,
+ totalMem.processStateSamples[i]);
+ }
+ }
+ totalPss = printMemoryCategory(pw, " ", "Cached ", totalMem.sysMemCachedWeight,
+ totalMem.totalTime, totalPss, totalMem.sysMemSamples);
+ totalPss = printMemoryCategory(pw, " ", "Free ", totalMem.sysMemFreeWeight,
+ totalMem.totalTime, totalPss, totalMem.sysMemSamples);
+ totalPss = printMemoryCategory(pw, " ", "Z-Ram ", totalMem.sysMemZRamWeight,
+ totalMem.totalTime, totalPss, totalMem.sysMemSamples);
+ pw.print(" TOTAL : ");
+ printSizeValue(pw, totalPss);
+ pw.println();
+ printMemoryCategory(pw, " ", STATE_NAMES[STATE_SERVICE_RESTARTING],
+ totalMem.processStateWeight[STATE_SERVICE_RESTARTING], totalMem.totalTime, totalPss,
+ totalMem.processStateSamples[STATE_SERVICE_RESTARTING]);
+ pw.println();
pw.print(" Start time: ");
pw.print(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimePeriodStartClock));
pw.println();
}
pw.print(' ');
pw.print(mRuntime);
- pw.print(' ');
- pw.print(mWebView);
pw.println();
}
public void dumpCheckinLocked(PrintWriter pw, String reqPackage) {
final long now = SystemClock.uptimeMillis();
final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
- pw.println("vers,4");
+ pw.println("vers,5");
pw.print("period,"); pw.print(mTimePeriodStartClockStr);
pw.print(","); pw.print(mTimePeriodStartRealtime); pw.print(",");
pw.print(mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime);
pw.print(",partial");
}
pw.println();
- pw.print("config,"); pw.print(mRuntime); pw.print(','); pw.println(mWebView);
+ pw.print("config,"); pw.println(mRuntime);
for (int ip=0; ip<pkgMap.size(); ip++) {
final String pkgName = pkgMap.keyAt(ip);
if (reqPackage != null && !reqPackage.equals(pkgName)) {
pw.print("total");
dumpAdjTimesCheckin(pw, ",", mMemFactorDurations, mMemFactor,
mStartTime, now);
+ if (mSysMemUsageTable != null) {
+ pw.print("sysmemusage");
+ for (int i=0; i<mSysMemUsageTableSize; i++) {
+ int off = mSysMemUsageTable[i];
+ int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+ pw.print(",");
+ printProcStateTag(pw, type);
+ for (int j=SYS_MEM_USAGE_SAMPLE_COUNT; j<SYS_MEM_USAGE_COUNT; j++) {
+ if (j > SYS_MEM_USAGE_CACHED_MINIMUM) {
+ pw.print(":");
+ }
+ pw.print(getLong(off, j));
+ }
+ }
+ }
+ pw.println();
+ TotalMemoryUseCollection totalMem = new TotalMemoryUseCollection(ALL_SCREEN_ADJ,
+ ALL_MEM_ADJ);
+ computeTotalMemoryUse(totalMem, now);
+ pw.print("weights,");
+ pw.print(totalMem.totalTime);
+ pw.print(",");
+ pw.print(totalMem.sysMemCachedWeight);
+ pw.print(":");
+ pw.print(totalMem.sysMemSamples);
+ pw.print(",");
+ pw.print(totalMem.sysMemFreeWeight);
+ pw.print(":");
+ pw.print(totalMem.sysMemSamples);
+ pw.print(",");
+ pw.print(totalMem.sysMemZRamWeight);
+ pw.print(":");
+ pw.print(totalMem.sysMemSamples);
+ pw.print(",");
+ pw.print(totalMem.sysMemKernelWeight);
+ pw.print(":");
+ pw.print(totalMem.sysMemSamples);
+ pw.print(",");
+ pw.print(totalMem.sysMemNativeWeight);
+ pw.print(":");
+ pw.print(totalMem.sysMemSamples);
+ for (int i=0; i<STATE_COUNT; i++) {
+ pw.print(",");
+ pw.print(totalMem.processStateWeight[i]);
+ pw.print(":");
+ pw.print(totalMem.processStateSamples[i]);
+ }
pw.println();
}
}
}
+ final public static class ProcessStateHolder {
+ public final int appVersion;
+ public ProcessStats.ProcessState state;
+
+ public ProcessStateHolder(int _appVersion) {
+ appVersion = _appVersion;
+ }
+ }
+
public static final class ProcessState extends DurationsTable {
public ProcessState mCommonProcess;
public final String mPackage;
* @param pkgList Processes to update.
*/
public void setState(int state, int memFactor, long now,
- ArrayMap<String, ProcessState> pkgList) {
+ ArrayMap<String, ProcessStateHolder> pkgList) {
if (state < 0) {
state = mNumStartedServices > 0
? (STATE_SERVICE_RESTARTING+(memFactor*STATE_COUNT)) : STATE_NOTHING;
}
public void addPss(long pss, long uss, boolean always,
- ArrayMap<String, ProcessState> pkgList) {
+ ArrayMap<String, ProcessStateHolder> pkgList) {
ensureNotDead();
if (!always) {
if (mLastPssState == mCurState && SystemClock.uptimeMillis()
}
}
- public void reportExcessiveWake(ArrayMap<String, ProcessState> pkgList) {
+ public void reportExcessiveWake(ArrayMap<String, ProcessStateHolder> pkgList) {
ensureNotDead();
mCommonProcess.mNumExcessiveWake++;
if (!mCommonProcess.mMultiPackage) {
}
}
- public void reportExcessiveCpu(ArrayMap<String, ProcessState> pkgList) {
+ public void reportExcessiveCpu(ArrayMap<String, ProcessStateHolder> pkgList) {
ensureNotDead();
mCommonProcess.mNumExcessiveCpu++;
if (!mCommonProcess.mMultiPackage) {
}
}
- public void reportCachedKill(ArrayMap<String, ProcessState> pkgList, long pss) {
+ public void reportCachedKill(ArrayMap<String, ProcessStateHolder> pkgList, long pss) {
ensureNotDead();
mCommonProcess.addCachedKill(1, pss, pss, pss);
if (!mCommonProcess.mMultiPackage) {
return this;
}
- private ProcessState pullFixedProc(ArrayMap<String, ProcessState> pkgList, int index) {
- ProcessState proc = pkgList.valueAt(index);
+ private ProcessState pullFixedProc(ArrayMap<String, ProcessStateHolder> pkgList,
+ int index) {
+ ProcessStateHolder holder = pkgList.valueAt(index);
+ ProcessState proc = holder.state;
if (mDead && proc.mCommonProcess != proc) {
// Somehow we are contining to use a process state that is dead, because
// it was not being told it was active during the last commit. We can recover
throw new IllegalStateException("Didn't create per-package process "
+ proc.mName + " in pkg " + pkg.mPackageName + "/" + pkg.mUid);
}
- pkgList.setValueAt(index, proc);
+ holder.state = proc;
}
return proc;
}
}
}
}
+
+ public static class TotalMemoryUseCollection {
+ final int[] screenStates;
+ final int[] memStates;
+
+ public TotalMemoryUseCollection(int[] _screenStates, int[] _memStates) {
+ screenStates = _screenStates;
+ memStates = _memStates;
+ }
+
+ public long totalTime;
+ public long[] processStatePss = new long[STATE_COUNT];
+ public double[] processStateWeight = new double[STATE_COUNT];
+ public long[] processStateTime = new long[STATE_COUNT];
+ public int[] processStateSamples = new int[STATE_COUNT];
+ public long[] sysMemUsage = new long[SYS_MEM_USAGE_COUNT];
+ public double sysMemCachedWeight;
+ public double sysMemFreeWeight;
+ public double sysMemZRamWeight;
+ public double sysMemKernelWeight;
+ public double sysMemNativeWeight;
+ public int sysMemSamples;
+ }
}
private File mFullRestoreSetDir;
private HashSet<String> mFullRestorePackages;
+ private FileInputStream mCurFullRestoreStream;
+ private FileOutputStream mFullRestoreSocketStream;
+ private byte[] mFullRestoreBuffer;
public LocalTransport(Context context) {
mContext = context;
}
}
+ @Override
public String name() {
return new ComponentName(mContext, this.getClass()).flattenToShortString();
}
+ @Override
public Intent configurationIntent() {
// The local transport is not user-configurable
return null;
}
+ @Override
public String currentDestinationString() {
return TRANSPORT_DESTINATION_STRING;
}
+ @Override
public String transportDirName() {
return TRANSPORT_DIR_NAME;
}
+ @Override
public long requestBackupTime() {
// any time is a good time for local backup
return 0;
}
+ @Override
public int initializeDevice() {
if (DEBUG) Log.v(TAG, "wiping all data");
deleteContents(mCurrentSetDir);
- return BackupTransport.TRANSPORT_OK;
+ return TRANSPORT_OK;
}
+ @Override
public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) {
if (DEBUG) {
try {
entity.write(buf, 0, dataSize);
} catch (IOException e) {
Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath());
- return BackupTransport.TRANSPORT_ERROR;
+ return TRANSPORT_ERROR;
} finally {
entity.close();
}
entityFile.delete();
}
}
- return BackupTransport.TRANSPORT_OK;
+ return TRANSPORT_OK;
} catch (IOException e) {
// oops, something went wrong. abort the operation and return error.
Log.v(TAG, "Exception reading backup input:", e);
- return BackupTransport.TRANSPORT_ERROR;
+ return TRANSPORT_ERROR;
}
}
}
}
+ @Override
public int clearBackupData(PackageInfo packageInfo) {
if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName);
packageDir.delete();
}
- return BackupTransport.TRANSPORT_OK;
+ return TRANSPORT_OK;
}
+ @Override
public int finishBackup() {
if (DEBUG) Log.v(TAG, "finishBackup()");
if (mSocket != null) {
mFullTargetPackage = null;
mSocket.close();
} catch (IOException e) {
- return BackupTransport.TRANSPORT_ERROR;
+ return TRANSPORT_ERROR;
} finally {
mSocket = null;
}
}
- return BackupTransport.TRANSPORT_OK;
+ return TRANSPORT_OK;
}
// ------------------------------------------------------------------------------------
// Full backup handling
+
+ @Override
public long requestFullBackupTime() {
return 0;
}
+ @Override
public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket) {
if (mSocket != null) {
Log.e(TAG, "Attempt to initiate full backup while one is in progress");
- return BackupTransport.TRANSPORT_ERROR;
+ return TRANSPORT_ERROR;
}
if (DEBUG) {
mSocketInputStream = new FileInputStream(mSocket.getFileDescriptor());
} catch (IOException e) {
Log.e(TAG, "Unable to process socket for full backup");
- return BackupTransport.TRANSPORT_ERROR;
+ return TRANSPORT_ERROR;
}
mFullTargetPackage = targetPackage.packageName;
File tarball = new File(mCurrentSetFullDir, mFullTargetPackage);
tarstream = new FileOutputStream(tarball);
} catch (FileNotFoundException e) {
- return BackupTransport.TRANSPORT_ERROR;
+ return TRANSPORT_ERROR;
}
mFullBackupOutputStream = new BufferedOutputStream(tarstream);
mFullBackupBuffer = new byte[4096];
- return BackupTransport.TRANSPORT_OK;
+ return TRANSPORT_OK;
}
+ @Override
public int sendBackupData(int numBytes) {
if (mFullBackupBuffer == null) {
Log.w(TAG, "Attempted sendBackupData before performFullBackup");
- return BackupTransport.TRANSPORT_ERROR;
+ return TRANSPORT_ERROR;
}
if (numBytes > mFullBackupBuffer.length) {
if (nRead < 0) {
// Something went wrong if we expect data but saw EOD
Log.w(TAG, "Unexpected EOD; failing backup");
- return BackupTransport.TRANSPORT_ERROR;
+ return TRANSPORT_ERROR;
}
mFullBackupOutputStream.write(mFullBackupBuffer, 0, nRead);
numBytes -= nRead;
} catch (IOException e) {
Log.e(TAG, "Error handling backup data for " + mFullTargetPackage);
- return BackupTransport.TRANSPORT_ERROR;
+ return TRANSPORT_ERROR;
}
}
- return BackupTransport.TRANSPORT_OK;
+ return TRANSPORT_OK;
}
// ------------------------------------------------------------------------------------
// Restore handling
static final long[] POSSIBLE_SETS = { 2, 3, 4, 5, 6, 7, 8, 9 };
+
+ @Override
public RestoreSet[] getAvailableRestoreSets() {
long[] existing = new long[POSSIBLE_SETS.length + 1];
int num = 0;
return available;
}
+ @Override
public long getCurrentRestoreSet() {
// The current restore set always has the same token
return CURRENT_SET_TOKEN;
}
+ @Override
public int startRestore(long token, PackageInfo[] packages) {
if (DEBUG) Log.v(TAG, "start restore " + token);
mRestorePackages = packages;
mRestoreSetDir = new File(mDataDir, Long.toString(token));
mRestoreSetIncrementalDir = new File(mRestoreSetDir, INCREMENTAL_DIR);
mRestoreSetFullDir = new File(mRestoreSetDir, FULL_DATA_DIR);
- return BackupTransport.TRANSPORT_OK;
+ return TRANSPORT_OK;
}
@Override
if (maybeFullData.length() > 0) {
if (DEBUG) Log.v(TAG, " nextRestorePackage(TYPE_FULL_STREAM) = " + name);
mRestoreType = RestoreDescription.TYPE_FULL_STREAM;
+ mCurFullRestoreStream = null; // ensure starting from the ground state
found = true;
}
}
return RestoreDescription.NO_MORE_PACKAGES;
}
+ @Override
public int getRestoreData(ParcelFileDescriptor outFd) {
if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called");
ArrayList<DecodedFilename> blobs = contentsByKey(packageDir);
if (blobs == null) { // nextRestorePackage() ensures the dir exists, so this is an error
Log.e(TAG, "No keys for package: " + packageDir);
- return BackupTransport.TRANSPORT_ERROR;
+ return TRANSPORT_ERROR;
}
// We expect at least some data if the directory exists in the first place
in.close();
}
}
- return BackupTransport.TRANSPORT_OK;
+ return TRANSPORT_OK;
} catch (IOException e) {
Log.e(TAG, "Unable to read backup records", e);
- return BackupTransport.TRANSPORT_ERROR;
+ return TRANSPORT_ERROR;
}
}
return contents;
}
+ @Override
public void finishRestore() {
if (DEBUG) Log.v(TAG, "finishRestore()");
+ if (mRestoreType == RestoreDescription.TYPE_FULL_STREAM) {
+ resetFullRestoreState();
+ }
+ mRestoreType = 0;
}
// ------------------------------------------------------------------------------------
// Full restore handling
- public int prepareFullRestore(long token, String[] targetPackages) {
- mRestoreSetDir = new File(mDataDir, Long.toString(token));
- mFullRestoreSetDir = new File(mRestoreSetDir, FULL_DATA_DIR);
- mFullRestorePackages = new HashSet<String>();
- if (mFullRestoreSetDir.exists()) {
- List<String> pkgs = Arrays.asList(mFullRestoreSetDir.list());
- HashSet<String> available = new HashSet<String>(pkgs);
-
- for (int i = 0; i < targetPackages.length; i++) {
- if (available.contains(targetPackages[i])) {
- mFullRestorePackages.add(targetPackages[i]);
- }
- }
+ private void resetFullRestoreState() {
+ try {
+ mCurFullRestoreStream.close();
+ } catch (IOException e) {
+ Log.w(TAG, "Unable to close full restore input stream");
}
- return BackupTransport.TRANSPORT_OK;
- }
-
- /**
- * Ask the transport what package's full data will be restored next. When all apps'
- * data has been delivered, the transport should return {@code null} here.
- * @return The package name of the next application whose data will be restored, or
- * {@code null} if all available package has been delivered.
- */
- public String getNextFullRestorePackage() {
- return null;
+ mCurFullRestoreStream = null;
+ mFullRestoreSocketStream = null;
+ mFullRestoreBuffer = null;
}
/**
* indicating a fatal error condition that precludes further restore operations
* on the current dataset.
*/
+ @Override
public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) {
- return 0;
+ if (mRestoreType != RestoreDescription.TYPE_FULL_STREAM) {
+ throw new IllegalStateException("Asked for full restore data for non-stream package");
+ }
+
+ // first chunk?
+ if (mCurFullRestoreStream == null) {
+ final String name = mRestorePackages[mRestorePackage].packageName;
+ if (DEBUG) Log.i(TAG, "Starting full restore of " + name);
+ File dataset = new File(mRestoreSetFullDir, name);
+ try {
+ mCurFullRestoreStream = new FileInputStream(dataset);
+ } catch (IOException e) {
+ // If we can't open the target package's tarball, we return the single-package
+ // error code and let the caller go on to the next package.
+ Log.e(TAG, "Unable to read archive for " + name);
+ return TRANSPORT_PACKAGE_REJECTED;
+ }
+ mFullRestoreSocketStream = new FileOutputStream(socket.getFileDescriptor());
+ mFullRestoreBuffer = new byte[32*1024];
+ }
+
+ int nRead;
+ try {
+ nRead = mCurFullRestoreStream.read(mFullRestoreBuffer);
+ if (nRead < 0) {
+ // EOF: tell the caller we're done
+ nRead = NO_MORE_DATA;
+ } else if (nRead == 0) {
+ // This shouldn't happen when reading a FileInputStream; we should always
+ // get either a positive nonzero byte count or -1. Log the situation and
+ // treat it as EOF.
+ Log.w(TAG, "read() of archive file returned 0; treating as EOF");
+ nRead = NO_MORE_DATA;
+ } else {
+ if (DEBUG) {
+ Log.i(TAG, " delivering restore chunk: " + nRead);
+ }
+ mFullRestoreSocketStream.write(mFullRestoreBuffer, 0, nRead);
+ }
+ } catch (IOException e) {
+ return TRANSPORT_ERROR; // Hard error accessing the file; shouldn't happen
+ } finally {
+ // Most transports will need to explicitly close 'socket' here, but this transport
+ // is in the same process as the caller so it can leave it up to the backup manager
+ // to manage both socket fds.
+ }
+
+ return nRead;
}
+
+ /**
+ * If the OS encounters an error while processing {@link RestoreDescription#TYPE_FULL_STREAM}
+ * data for restore, it will invoke this method to tell the transport that it should
+ * abandon the data download for the current package. The OS will then either call
+ * {@link #nextRestorePackage()} again to move on to restoring the next package in the
+ * set being iterated over, or will call {@link #finishRestore()} to shut down the restore
+ * operation.
+ *
+ * @return {@link #TRANSPORT_OK} if the transport was successful in shutting down the
+ * current stream cleanly, or {@link #TRANSPORT_ERROR} to indicate a serious
+ * transport-level failure. If the transport reports an error here, the entire restore
+ * operation will immediately be finished with no further attempts to restore app data.
+ */
+ @Override
+ public int abortFullRestore() {
+ if (mRestoreType != RestoreDescription.TYPE_FULL_STREAM) {
+ throw new IllegalStateException("abortFullRestore() but not currently restoring");
+ }
+ resetFullRestoreState();
+ mRestoreType = 0;
+ return TRANSPORT_OK;
+ }
+
}
final StopwatchTimer[] mBluetoothStateTimer = new StopwatchTimer[NUM_BLUETOOTH_STATES];
int mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
+ long mMobileRadioActiveStartTime;
StopwatchTimer mMobileRadioActiveTimer;
StopwatchTimer mMobileRadioActivePerAppTimer;
LongSamplingCounter mMobileRadioActiveAdjustedTime;
return 0;
}
- long getLastUpdateTimeMs() {
- return mUpdateTime;
- }
-
void stopRunningLocked(long elapsedRealtimeMs) {
// Ignore attempt to stop a timer that isn't running
if (mNesting == 0) {
powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_MEDIUM
|| powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH;
if (active) {
- realElapsedRealtimeMs = elapsedRealtime;
+ mMobileRadioActiveStartTime = realElapsedRealtimeMs = elapsedRealtime;
mHistoryCur.states |= HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
} else {
realElapsedRealtimeMs = timestampNs / (1000*1000);
- long lastUpdateTimeMs = mMobileRadioActiveTimer.getLastUpdateTimeMs();
+ long lastUpdateTimeMs = mMobileRadioActiveStartTime;
if (realElapsedRealtimeMs < lastUpdateTimeMs) {
Slog.wtf(TAG, "Data connection inactive timestamp " + realElapsedRealtimeMs
+ " is before start time " + lastUpdateTimeMs);
// connect to camera service
static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
- jobject weak_this, jint cameraId, jstring clientPackageName)
+ jobject weak_this, jint cameraId, jint halVersion, jstring clientPackageName)
{
// Convert jstring to String16
const char16_t *rawClientName = env->GetStringChars(clientPackageName, NULL);
String16 clientName(rawClientName, rawClientNameLen);
env->ReleaseStringChars(clientPackageName, rawClientName);
- sp<Camera> camera = Camera::connect(cameraId, clientName,
- Camera::USE_CALLING_UID);
+ sp<Camera> camera;
+ if (halVersion == ICameraService::CAMERA_HAL_API_VERSION_UNSPECIFIED) {
+ // Default path: hal version is unspecified, do normal camera open.
+ camera = Camera::connect(cameraId, clientName,
+ Camera::USE_CALLING_UID);
+ } else {
+ jint status = Camera::connectLegacy(cameraId, halVersion, clientName,
+ Camera::USE_CALLING_UID, camera);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
if (camera == NULL) {
return -EACCES;
// finalizer is invoked later.
static void android_hardware_Camera_release(JNIEnv *env, jobject thiz)
{
- // TODO: Change to ALOGV
ALOGV("release camera");
JNICameraContext* context = NULL;
sp<Camera> camera;
"(ILandroid/hardware/Camera$CameraInfo;)V",
(void*)android_hardware_Camera_getCameraInfo },
{ "native_setup",
- "(Ljava/lang/Object;ILjava/lang/String;)I",
+ "(Ljava/lang/Object;IILjava/lang/String;)I",
(void*)android_hardware_Camera_native_setup },
{ "native_release",
"()V",
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true"
android:multiprocess="true"
- android:documentLaunchMode="never">
+ android:documentLaunchMode="never"
+ android:relinquishTaskIdentity="true">
<intent-filter>
<action android:name="android.intent.action.CHOOSER" />
<category android:name="android.intent.category.DEFAULT" />
Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS} -->
<attr name="autoRemoveFromRecents" format="boolean" />
+ <!-- Tasks whose root has this attribute set to true will replace baseIntent with that of the
+ next activity in the task. If the next activity also has this attribute set to true then
+ it will yield the baseIntent to any activity that it launches in the same task. This
+ continues until an activity is encountered which has this attribute set to false. False
+ is the default. This attribute set to true also permits activity's use of the
+ TaskDescription to change labels, colors and icons in the recent task list. -->
+ <attr name="relinquishTaskIdentity" format="boolean" />
+
<!-- The <code>manifest</code> tag is the root of an
<code>AndroidManifest.xml</code> file,
describing the contents of an Android package (.apk) file. One
<attr name="documentLaunchMode" />
<attr name="maxRecents" />
<attr name="autoRemoveFromRecents" />
+ <attr name="relinquishTaskIdentity" />
</declare-styleable>
<!-- The <code>activity-alias</code> tag declares a new
<public type="attr" name="fullBackupOnly" />
<public type="attr" name="propertyXName" />
<public type="attr" name="propertyYName" />
+ <public type="attr" name="relinquishTaskIdentity" />
<public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
}
}
+ @SmallTest
+ public void testConnectLegacy() throws Exception {
+ final int CAMERA_HAL_API_VERSION_1_0 = 0x100;
+ for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
+ ICamera cameraUser = null;
+ ICameraClient dummyCallbacks = new DummyCameraClient();
+
+ String clientPackageName = getContext().getPackageName();
+
+ BinderHolder holder = new BinderHolder();
+
+ try {
+ CameraBinderDecorator.newInstance(mUtils.getCameraService())
+ .connectLegacy(dummyCallbacks, cameraId, CAMERA_HAL_API_VERSION_1_0,
+ clientPackageName,
+ CameraBinderTestUtils.USE_CALLING_UID, holder);
+ cameraUser = ICamera.Stub.asInterface(holder.getBinder());
+ assertNotNull(String.format("Camera %s was null", cameraId), cameraUser);
+
+ Log.v(TAG, String.format("Camera %s connected as HAL1 legacy device", cameraId));
+ } catch (RuntimeException e) {
+ // Not all camera device support openLegacy.
+ Log.i(TAG, "Unable to open camera as HAL1 legacy camera device " + e);
+ } finally {
+ if (cameraUser != null) {
+ cameraUser.disconnect();
+ }
+ }
+ }
+ }
+
static class DummyCameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
/*
--- /dev/null
+/*
+ * 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.mediaframeworktest.unit;
+
+import android.hardware.Camera;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+/**
+ * <pre>
+ * adb shell am instrument \
+ * -e class 'com.android.mediaframeworktest.unit.CameraOpenTest' \
+ * -w com.android.mediaframeworktest/.MediaFrameworkUnitTestRunner
+ * </pre>
+ */
+public class CameraOpenTest extends junit.framework.TestCase {
+ private static String TAG = "CameraOpenTest";
+
+ private Camera mCamera;
+
+ /**
+ * Test @hide android.hardware.Camera#openLegacy API that cannot be tested in CTS.
+ */
+ @SmallTest
+ public void testOpenLegacy() {
+ int nCameras = Camera.getNumberOfCameras();
+ for (int id = 0; id < nCameras; id++) {
+ try {
+ mCamera.openLegacy(id, Camera.CAMERA_HAL_API_VERSION_1_0);
+ } catch (RuntimeException e) {
+ Log.i(TAG, "Unable to open camera as HAL1 legacy camera device " + e);
+ } finally {
+ if (mCamera != null) {
+ mCamera.release();
+ }
+ }
+ }
+ }
+}
android:viewportWidth="24.0"
android:viewportHeight="24.0"/>
+ <group
+ android:scaleX="1.2"
+ android:scaleY="1.2"
+ android:pivotX="12.0"
+ android:pivotY="12.0">
<path
android:fill="#FFFFFFFF"
android:pathData="M12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0c5.5,0.0 10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM12.0,5.0c1.7,0.0 3.0,1.3 3.0,3.0c0.0,1.7 -1.3,3.0 -3.0,3.0c-1.7,0.0 -3.0,-1.3 -3.0,-3.0C9.0,6.3 10.3,5.0 12.0,5.0zM12.0,19.2c-2.5,0.0 -4.7,-1.3 -6.0,-3.2c0.0,-2.0 4.0,-3.1 6.0,-3.1c2.0,0.0 6.0,1.1 6.0,3.1C16.7,17.9 14.5,19.2 12.0,19.2z"/>
+ </group>
</vector>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_user_switcher"
+ android:orientation="vertical"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:gravity="end"
+ android:visibility="gone"
+ android:paddingTop="4dp">
+</LinearLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ 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
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:sysui="http://schemas.android.com/apk/res-auto"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="8dp"
+ android:layout_marginEnd="8dp"
+ android:gravity="center_vertical"
+ android:clickable="true"
+ android:background="@drawable/ripple_drawable">
+ <TextView android:id="@+id/name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="16dp"
+ android:textAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.UserName"
+ />
+ <com.android.systemui.statusbar.phone.UserAvatarView android:id="@+id/picture"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:contentDescription="@null"
+ sysui:frameWidth="@dimen/keyguard_user_switcher_border_thickness"
+ sysui:activeFrameColor="@color/current_user_border_color" />
+</LinearLayout>
\ No newline at end of file
android:id="@+id/notification_panel"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:background="@android:color/transparent"
>
<include
<include layout="@layout/status_bar_expanded_header" />
+ <ViewStub
+ android:id="@+id/keyguard_user_switcher"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_marginTop="@dimen/status_bar_header_height_keyguard"
+ android:layout_gravity="end"
+ android:layout="@layout/keyguard_user_switcher" />
+
<include
layout="@layout/keyguard_bottom_area"
android:visibility="gone" />
android:layout_width="wrap_content"
android:layout_height="@dimen/status_bar_header_height"
android:layout_toStartOf="@id/multi_user_switch"
- android:layout_marginEnd="2dp"
+ android:layout_alignWithParentIfMissing="true"
android:layout_marginStart="16dp"
- />
+ android:paddingEnd="2dp" />
<TextView
android:id="@+id/header_charging_info"
<!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
card. -->
<integer name="keyguard_max_notification_count">5</integer>
+
+ <!-- Set to true to enable the user switcher on the keyguard. -->
+ <bool name="config_keyguardUserSwitcher">true</bool>
</resources>
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
</attr>
+ <declare-styleable name="UserAvatarView">
+ <attr name="frameWidth" format="dimension" />
+ <attr name="activeFrameColor" format="color" />
+ <attr name="frameColor" />
+ </declare-styleable>
</resources>
<color name="keyguard_affordance">#ffffffff</color>
+ <!-- The color of the circle around the primary user in the user switcher -->
+ <color name="current_user_border_color">@color/primary_color</color>
+
<!-- Our material color palette (deep teal) -->
<color name="primary_color">#ff7fcac3</color>
<color name="background_color_1">#ff384248</color>
Notification.tickerText across the status bar for what seems like an
eternity. -->
<bool name="enable_ticker">false</bool>
+
+ <!-- Set to true to enable the user switcher on the keyguard. -->
+ <bool name="config_keyguardUserSwitcher">false</bool>
</resources>
<!-- end margin for multi user switch in expanded quick settings -->
<dimen name="multi_user_switch_expanded_margin">8dp</dimen>
+
+ <!-- end margin for system icons if multi user switch is hidden -->
+ <dimen name="system_icons_switcher_hidden_expanded_margin">16dp</dimen>
+
+ <!-- The thickness of the colored border around the current user. -->
+ <dimen name="keyguard_user_switcher_border_thickness">2dp</dimen>
+
</resources>
<style name="TextAppearance.StatusBar.Expanded.Network.EmergencyOnly">
</style>
+ <style name="TextAppearance.StatusBar.Expanded.UserSwitcher">
+ <item name="android:textSize">16sp</item>
+ <item name="android:textStyle">normal</item>
+ <item name="android:textColor">#ffffff</item>
+ </style>
+ <style name="TextAppearance.StatusBar.Expanded.UserSwitcher.UserName" />
+
<style name="TextAppearance" />
<style name="TextAppearance.QuickSettings" />
import com.android.systemui.statusbar.policy.CastControllerImpl;
import com.android.systemui.statusbar.policy.DateView;
import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
+import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.LocationControllerImpl;
import com.android.systemui.statusbar.policy.NetworkControllerImpl;
ZenModeController mZenModeController;
CastControllerImpl mCastController;
VolumeComponent mVolumeComponent;
+ KeyguardUserSwitcher mKeyguardUserSwitcher;
int mNaturalBarHeight = -1;
int mIconSize = -1;
final SignalClusterView signalCluster =
(SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster);
+ mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
+ (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher), mHeader);
mNetworkController.addSignalCluster(signalCluster);
signalCluster.setNetworkController(mNetworkController);
mKeyguardStatusView.setVisibility(View.VISIBLE);
mKeyguardIndicationController.setVisible(true);
mNotificationPanel.resetViews();
+ mKeyguardUserSwitcher.setKeyguard(true);
} else {
mKeyguardStatusView.setVisibility(View.GONE);
mKeyguardIndicationController.setVisible(false);
+ mKeyguardUserSwitcher.setKeyguard(false);
}
if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
mKeyguardBottomArea.setVisibility(View.VISIBLE);
private boolean mShowEmergencyCallsOnly;
private boolean mShowChargingInfo;
+ private boolean mKeyguardUserSwitcherShowing;
private int mCollapsedHeight;
private int mExpandedHeight;
private int mNormalWidth;
private int mPadding;
private int mMultiUserExpandedMargin;
+ private int mSystemIconsSwitcherHiddenExpandedMargin;
private ActivityStarter mActivityStarter;
private BrightnessController mBrightnessController;
mPadding = getResources().getDimensionPixelSize(R.dimen.notification_side_padding);
mMultiUserExpandedMargin =
getResources().getDimensionPixelSize(R.dimen.multi_user_switch_expanded_margin);
-
+ mSystemIconsSwitcherHiddenExpandedMargin = getResources().getDimensionPixelSize(
+ R.dimen.system_icons_switcher_hidden_expanded_margin);
}
public void setActivityStarter(ActivityStarter activityStarter) {
? VISIBLE : GONE);
mChargingInfo.setVisibility(mExpanded && !mOverscrolled && mShowChargingInfo
&& !mShowEmergencyCallsOnly ? VISIBLE : GONE);
+ mMultiUserSwitch.setVisibility(mExpanded || !mKeyguardUserSwitcherShowing
+ ? VISIBLE : GONE);
}
private void updateSystemIconsLayoutParams() {
RelativeLayout.LayoutParams lp = (LayoutParams) mSystemIconsContainer.getLayoutParams();
boolean systemIconsAboveClock = mExpanded && !mOverscrolled
&& mShowChargingInfo && !mShowEmergencyCallsOnly;
+ lp.setMarginEnd(0);
if (systemIconsAboveClock) {
lp.addRule(ALIGN_PARENT_START);
lp.removeRule(START_OF);
? mSettingsButton.getId()
: mMultiUserSwitch.getId());
lp.removeRule(ALIGN_PARENT_START);
+ if (mMultiUserSwitch.getVisibility() == GONE) {
+ lp.setMarginEnd(mSystemIconsSwitcherHiddenExpandedMargin);
+ }
}
+ mSystemIconsContainer.setLayoutParams(lp);
RelativeLayout.LayoutParams clockLp = (LayoutParams) mDateTime.getLayoutParams();
if (systemIconsAboveClock) {
} else {
clockLp.addRule(BELOW, mEmergencyCallsOnly.getId());
}
+ mDateTime.setLayoutParams(clockLp);
}
private void updateBrightnessControllerState() {
public void setChargingInfo(CharSequence chargingInfo) {
mChargingInfo.setText(chargingInfo);
}
+
+ public void setKeyguardUserSwitcherShowing(boolean showing) {
+ // STOPSHIP: NOT CALLED PROPERLY WHEN GOING TO FULL SHADE AND RETURNING!?!
+ mKeyguardUserSwitcherShowing = showing;
+ updateVisibilities();
+ updateSystemIconsLayoutParams();
+ }
}
--- /dev/null
+/*
+ * 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.systemui.statusbar.phone;
+
+import com.android.systemui.R;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * A view that displays a user image cropped to a circle with a frame.
+ */
+public class UserAvatarView extends View {
+
+ private int mActiveFrameColor;
+ private int mFrameColor;
+ private float mFrameWidth;
+ private Bitmap mBitmap;
+ private Drawable mDrawable;
+
+ private final Paint mFramePaint = new Paint();
+ private final Paint mBitmapPaint = new Paint();
+ private final Matrix mDrawMatrix = new Matrix();
+
+ private float mScale = 1;
+
+ public UserAvatarView(Context context, AttributeSet attrs,
+ int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ final TypedArray a = context.obtainStyledAttributes(
+ attrs, R.styleable.UserAvatarView, defStyleAttr, defStyleRes);
+ final int N = a.getIndexCount();
+ for (int i = 0; i < N; i++) {
+ int attr = a.getIndex(i);
+ switch (attr) {
+ case R.styleable.UserAvatarView_frameWidth:
+ setFrameWidth(a.getDimension(attr, 0));
+ break;
+ case R.styleable.UserAvatarView_activeFrameColor:
+ setActiveFrameColor(a.getColor(attr, 0));
+ break;
+ case R.styleable.UserAvatarView_frameColor:
+ setFrameColor(a.getColor(attr, 0));
+ break;
+ }
+ }
+ a.recycle();
+
+ mFramePaint.setAntiAlias(true);
+ mFramePaint.setStyle(Paint.Style.STROKE);
+ mBitmapPaint.setAntiAlias(true);
+ }
+
+ public UserAvatarView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public UserAvatarView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public UserAvatarView(Context context) {
+ this(context, null);
+ }
+
+ public void setBitmap(Bitmap bitmap) {
+ setDrawable(null);
+ mBitmap = bitmap;
+ mBitmapPaint.setShader(new BitmapShader(
+ bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
+ configureBounds();
+ invalidate();
+ }
+
+ public void setFrameColor(int frameColor) {
+ mFrameColor = frameColor;
+ invalidate();
+ }
+
+ public void setActiveFrameColor(int activeFrameColor) {
+ mActiveFrameColor = activeFrameColor;
+ invalidate();
+ }
+
+ public void setFrameWidth(float frameWidth) {
+ mFrameWidth = frameWidth;
+ invalidate();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ configureBounds();
+ }
+
+ public void configureBounds() {
+ int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
+ int vheight = getHeight() - mPaddingTop - mPaddingBottom;
+
+ int dwidth;
+ int dheight;
+ if (mBitmap != null) {
+ dwidth = mBitmap.getWidth();
+ dheight = mBitmap.getHeight();
+ } else if (mDrawable != null) {
+ dwidth = mDrawable.getIntrinsicWidth();
+ dheight = mDrawable.getIntrinsicHeight();
+ mDrawable.setBounds(0, 0, dwidth, dheight);
+ vwidth -= 2 * (mFrameWidth - 1);
+ vheight -= 2 * (mFrameWidth - 1);
+ } else {
+ return;
+ }
+
+ float scale;
+ float dx;
+ float dy;
+
+ scale = Math.min((float) vwidth / (float) dwidth,
+ (float) vheight / (float) dheight);
+
+ dx = (int) ((vwidth - dwidth * scale) * 0.5f + 0.5f);
+ dy = (int) ((vheight - dheight * scale) * 0.5f + 0.5f);
+
+ mDrawMatrix.setScale(scale, scale);
+ mDrawMatrix.postTranslate(dx, dy);
+ mScale = scale;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ int frameColor = isActivated() ? mActiveFrameColor : mFrameColor;
+ float halfW = getWidth() / 2f;
+ float halfH = getHeight() / 2f;
+ float halfSW = Math.min(halfH, halfW);
+ if (mBitmap != null && mScale > 0) {
+ int saveCount = canvas.getSaveCount();
+ canvas.save();
+ canvas.translate(mPaddingLeft, mPaddingTop);
+ canvas.concat(mDrawMatrix);
+ float halfBW = mBitmap.getWidth() / 2f;
+ float halfBH = mBitmap.getHeight() / 2f;
+ float halfBSW = Math.min(halfBH, halfBW);
+ canvas.drawCircle(halfBW, halfBH, halfBSW - mFrameWidth / mScale + 1, mBitmapPaint);
+ canvas.restoreToCount(saveCount);
+ } else if (mDrawable != null && mScale > 0) {
+ int saveCount = canvas.getSaveCount();
+ canvas.save();
+ canvas.translate(mPaddingLeft, mPaddingTop);
+ canvas.translate(mFrameWidth - 1, mFrameWidth - 1);
+ canvas.concat(mDrawMatrix);
+ mDrawable.draw(canvas);
+ canvas.restoreToCount(saveCount);
+ }
+ if (frameColor != 0) {
+ mFramePaint.setColor(frameColor);
+ mFramePaint.setStrokeWidth(mFrameWidth);
+ canvas.drawCircle(halfW, halfH, halfSW - mFrameWidth / 2f, mFramePaint);
+ }
+ }
+
+ public void setDrawable(Drawable d) {
+ if (mDrawable != null) {
+ mDrawable.setCallback(null);
+ unscheduleDrawable(mDrawable);
+ }
+ mDrawable = d;
+ if (d != null) {
+ d.setCallback(this);
+ if (d.isStateful()) {
+ d.setState(getDrawableState());
+ }
+ d.setLayoutDirection(getLayoutDirection());
+ configureBounds();
+ }
+ if (d != null) {
+ mBitmap = null;
+ }
+ configureBounds();
+ invalidate();
+ }
+
+ @Override
+ public void invalidateDrawable(Drawable dr) {
+ if (dr == mDrawable) {
+ invalidate();
+ } else {
+ super.invalidateDrawable(dr);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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.systemui.statusbar.policy;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.StatusBarHeaderView;
+import com.android.systemui.statusbar.phone.UserAvatarView;
+
+import android.app.ActivityManagerNative;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.graphics.Bitmap;
+import android.os.AsyncTask;
+import android.os.RemoteException;
+import android.os.UserManager;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewStub;
+import android.view.WindowManagerGlobal;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Manages the user switcher on the Keyguard.
+ */
+public class KeyguardUserSwitcher implements View.OnClickListener {
+
+ private static final String TAG = "KeyguardUserSwitcher";
+
+ private final Context mContext;
+ private final ViewGroup mUserSwitcher;
+ private final UserManager mUserManager;
+ private final StatusBarHeaderView mHeader;
+
+ public KeyguardUserSwitcher(Context context, ViewStub userSwitcher,
+ StatusBarHeaderView header) {
+ mContext = context;
+ if (context.getResources().getBoolean(R.bool.config_keyguardUserSwitcher)) {
+ mUserSwitcher = (ViewGroup) userSwitcher.inflate();
+ mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ mHeader = header;
+ refresh();
+ } else {
+ mUserSwitcher = null;
+ mUserManager = null;
+ mHeader = null;
+ }
+ }
+
+ public void setKeyguard(boolean keyguard) {
+ if (mUserSwitcher != null) {
+ // TODO: Cache showUserSwitcherOnKeyguard().
+ if (keyguard && showUserSwitcherOnKeyguard()) {
+ show();
+ refresh();
+ } else {
+ hide();
+ }
+ }
+ }
+
+ /**
+ * @return true if the user switcher should be shown on the lock screen.
+ * @see android.os.UserManager#isUserSwitcherEnabled()
+ */
+ private boolean showUserSwitcherOnKeyguard() {
+ // TODO: Set isEdu. The edu provisioning process can add settings to Settings.Global.
+ boolean isEdu = false;
+ if (isEdu) {
+ return true;
+ }
+ List<UserInfo> users = mUserManager.getUsers(true /* excludeDying */);
+ int N = users.size();
+ int switchableUsers = 0;
+ for (int i = 0; i < N; i++) {
+ if (users.get(i).supportsSwitchTo()) {
+ switchableUsers++;
+ }
+ }
+ return switchableUsers > 1;
+ }
+
+ public void show() {
+ if (mUserSwitcher != null) {
+ // TODO: animate
+ mUserSwitcher.setVisibility(View.VISIBLE);
+ mHeader.setKeyguardUserSwitcherShowing(true);
+ }
+ }
+
+ private void hide() {
+ if (mUserSwitcher != null) {
+ // TODO: animate
+ mUserSwitcher.setVisibility(View.GONE);
+ mHeader.setKeyguardUserSwitcherShowing(false);
+ }
+ }
+
+ private void refresh() {
+ if (mUserSwitcher != null) {
+ new AsyncTask<Void, Void, ArrayList<UserData>>() {
+ @Override
+ protected ArrayList<UserData> doInBackground(Void... params) {
+ return loadUsers();
+ }
+
+ @Override
+ protected void onPostExecute(ArrayList<UserData> userInfos) {
+ bind(userInfos);
+ }
+ }.execute((Void[]) null);
+ }
+ }
+
+ private void bind(ArrayList<UserData> userList) {
+ mUserSwitcher.removeAllViews();
+ int N = userList.size();
+ for (int i = 0; i < N; i++) {
+ mUserSwitcher.addView(inflateUser(userList.get(i)));
+ }
+ // TODO: add Guest
+ // TODO: add (+) button
+ }
+
+ private View inflateUser(UserData user) {
+ View v = LayoutInflater.from(mUserSwitcher.getContext()).inflate(
+ R.layout.keyguard_user_switcher_item, mUserSwitcher, false);
+ TextView name = (TextView) v.findViewById(R.id.name);
+ UserAvatarView picture = (UserAvatarView) v.findViewById(R.id.picture);
+ name.setText(user.userInfo.name);
+ picture.setActivated(user.isCurrent);
+ if (user.userInfo.isGuest()) {
+ picture.setDrawable(mContext.getResources().getDrawable(R.drawable.ic_account_circle));
+ } else {
+ picture.setBitmap(user.userIcon);
+ }
+ v.setOnClickListener(this);
+ v.setTag(user.userInfo);
+ // TODO: mark which user is current for accessibility.
+ return v;
+ }
+
+ @Override
+ public void onClick(View v) {
+ switchUser(((UserInfo)v.getTag()).id);
+ }
+
+ // TODO: Factor out logic below and share with QS implementation.
+
+ private ArrayList<UserData> loadUsers() {
+ ArrayList<UserInfo> users = (ArrayList<UserInfo>) mUserManager
+ .getUsers(true /* excludeDying */);
+ int N = users.size();
+ ArrayList<UserData> result = new ArrayList<>(N);
+ int currentUser = -1;
+ try {
+ currentUser = ActivityManagerNative.getDefault().getCurrentUser().id;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couln't get current user.", e);
+ }
+ for (int i = 0; i < N; i++) {
+ UserInfo user = users.get(i);
+ if (user.supportsSwitchTo()) {
+ boolean isCurrent = user.id == currentUser;
+ result.add(new UserData(user, mUserManager.getUserIcon(user.id), isCurrent));
+ }
+ }
+ return result;
+ }
+
+ private void switchUser(int userId) {
+ try {
+ WindowManagerGlobal.getWindowManagerService().lockNow(null);
+ ActivityManagerNative.getDefault().switchUser(userId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't switch user.", e);
+ }
+ }
+
+ private static class UserData {
+ final UserInfo userInfo;
+ final Bitmap userIcon;
+ final boolean isCurrent;
+
+ UserData(UserInfo userInfo, Bitmap userIcon, boolean isCurrent) {
+ this.userInfo = userInfo;
+ this.userIcon = userIcon;
+ this.isCurrent = isCurrent;
+ }
+ }
+}
+ " app=" + app);
if (app != null && app.thread != null) {
try {
- app.addPackage(r.appInfo.packageName, mAm.mProcessStats);
+ app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
realStartServiceLocked(r, app, execInFg);
return null;
} catch (RemoteException e) {
mPendingServices.remove(i);
i--;
- proc.addPackage(sr.appInfo.packageName, mAm.mProcessStats);
+ proc.addPackage(sr.appInfo.packageName, sr.appInfo.versionCode,
+ mAm.mProcessStats);
realStartServiceLocked(sr, proc, sr.createdFromFg);
didSomething = true;
}
long mLastFullPssTime = SystemClock.uptimeMillis();
/**
+ * If set, the next time we collect PSS data we should do a full collection
+ * with data from native processes and the kernel.
+ */
+ boolean mFullPssPending = false;
+
+ /**
* This is the process holding what we currently consider to be
* the "home" activity.
*/
public void handleMessage(Message msg) {
switch (msg.what) {
case COLLECT_PSS_BG_MSG: {
- int i=0, num=0;
long start = SystemClock.uptimeMillis();
+ MemInfoReader memInfo = null;
+ synchronized (ActivityManagerService.this) {
+ if (mFullPssPending) {
+ mFullPssPending = false;
+ memInfo = new MemInfoReader();
+ }
+ }
+ if (memInfo != null) {
+ updateCpuStatsNow();
+ long nativeTotalPss = 0;
+ synchronized (mProcessCpuThread) {
+ final int N = mProcessCpuTracker.countStats();
+ for (int j=0; j<N; j++) {
+ ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(j);
+ if (st.vsize <= 0 || st.uid >= Process.FIRST_APPLICATION_UID
+ || st.uid == Process.SYSTEM_UID) {
+ // This is definitely an application process; skip it.
+ continue;
+ }
+ synchronized (mPidsSelfLocked) {
+ if (mPidsSelfLocked.indexOfKey(st.pid) >= 0) {
+ // This is one of our own processes; skip it.
+ continue;
+ }
+ }
+ nativeTotalPss += Debug.getPss(st.pid, null);
+ }
+ }
+ memInfo.readMemInfo();
+ synchronized (this) {
+ if (DEBUG_PSS) Slog.d(TAG, "Collected native and kernel memory in "
+ + (SystemClock.uptimeMillis()-start) + "ms");
+ mProcessStats.addSysMemUsageLocked(memInfo.getCachedSizeKb(),
+ memInfo.getFreeSizeKb(),
+ memInfo.getSwapTotalSizeKb()-memInfo.getSwapFreeSizeKb(),
+ memInfo.getBuffersSizeKb()+memInfo.getShmemSizeKb()
+ +memInfo.getSlabSizeKb(),
+ nativeTotalPss);
+ }
+ }
+
+ int i=0, num=0;
long[] tmp = new long[1];
do {
ProcessRecord proc;
// come up (we have a pid but not yet its thread), so keep it.
if (DEBUG_PROCESSES) Slog.v(TAG, "App already running: " + app);
// If this is a new package in the process, add the package to the list
- app.addPackage(info.packageName, mProcessStats);
+ app.addPackage(info.packageName, info.versionCode, mProcessStats);
return app;
}
}
} else {
// If this is a new package in the process, add the package to the list
- app.addPackage(info.packageName, mProcessStats);
+ app.addPackage(info.packageName, info.versionCode, mProcessStats);
}
// If the system is not ready yet, then hold off on starting this
// to run in multiple processes, because this is actually
// part of the framework so doesn't make sense to track as a
// separate apk in the process.
- app.addPackage(cpi.applicationInfo.packageName, mProcessStats);
+ app.addPackage(cpi.applicationInfo.packageName, cpi.applicationInfo.versionCode,
+ mProcessStats);
}
ensurePackageDexOpt(cpi.applicationInfo.packageName);
}
}
}
+ long nativeProcTotalPss = 0;
+
if (!isCheckinRequest && procs.size() > 1) {
// If we are showing aggregations, also look for native processes to
// include so that our aggregations are more accurate.
final long myTotalPss = mi.getTotalPss();
totalPss += myTotalPss;
+ nativeProcTotalPss += myTotalPss;
MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")",
st.name, myTotalPss, st.pid, false);
}
MemInfoReader memInfo = new MemInfoReader();
memInfo.readMemInfo();
+ if (nativeProcTotalPss > 0) {
+ synchronized (this) {
+ mProcessStats.addSysMemUsageLocked(memInfo.getCachedSizeKb(),
+ memInfo.getFreeSizeKb(),
+ memInfo.getSwapTotalSizeKb()-memInfo.getSwapFreeSizeKb(),
+ memInfo.getBuffersSizeKb()+memInfo.getShmemSizeKb()+memInfo.getSlabSizeKb(),
+ nativeProcTotalPss);
+ }
+ }
if (!brief) {
if (!isCompact) {
pw.print("Total RAM: "); pw.print(memInfo.getTotalSizeKb());
}
if (DEBUG_PSS) Slog.d(TAG, "Requesting PSS of all procs! memLowered=" + memLowered);
mLastFullPssTime = now;
+ mFullPssPending = true;
mPendingPssProcesses.ensureCapacity(mLruProcesses.size());
mPendingPssProcesses.clear();
for (int i=mLruProcesses.size()-1; i>=0; i--) {
}
}
- boolean isRootActivity() {
- final ArrayList<ActivityRecord> activities = task.mActivities;
- return activities.size() == 0 || this == activities.get(0);
- }
-
UriPermissionOwner getUriPermissionsLocked() {
if (uriPermissions == null) {
uriPermissions = new UriPermissionOwner(service, this);
return -1;
}
final TaskRecord task = r.task;
- switch (task.mActivities.indexOf(r)) {
- case -1: return -1;
- case 0: return task.taskId;
- default: return onlyRoot ? -1 : task.taskId;
+ final int activityNdx = task.mActivities.indexOf(r);
+ if (activityNdx < 0 || (onlyRoot && activityNdx > task.findEffectiveRootIndex())) {
+ return -1;
}
+ return task.taskId;
}
static ActivityRecord isInStackLocked(IBinder token) {
}
}
- /**
- * Determine if home should be visible below the passed record.
- * @param record activity we are querying for.
- * @return true if home is visible below the passed activity, false otherwise.
- */
- boolean isActivityOverHome(ActivityRecord record) {
- // Start at record and go down, look for either home or a visible fullscreen activity.
- final TaskRecord recordTask = record.task;
- for (int taskNdx = mTaskHistory.indexOf(recordTask); taskNdx >= 0; --taskNdx) {
- TaskRecord task = mTaskHistory.get(taskNdx);
- final ArrayList<ActivityRecord> activities = task.mActivities;
- final int startNdx =
- task == recordTask ? activities.indexOf(record) : activities.size() - 1;
- for (int activityNdx = startNdx; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
- if (r.isHomeActivity()) {
- return true;
- }
- if (!r.finishing && r.fullscreen) {
- // Passed activity is over a fullscreen activity.
- return false;
- }
- }
- if (task.mOnTopOfHome) {
- // Got to the bottom of a task on top of home without finding a visible fullscreen
- // activity. Home is visible.
- return true;
- }
- }
- // Got to the bottom of this stack and still don't know. If this is over the home stack
- // then record is over home. May not work if we ever get more than two layers.
- return mStackSupervisor.isFrontStack(this);
- }
-
private void setVisibile(ActivityRecord r, boolean visible) {
r.visible = visible;
mWindowManager.setAppVisibility(r.appToken, visible);
// existing activities from other tasks in to it.
// If the caller has requested that the target task be
// reset, then do so.
- if ((r.intent.getFlags()
- & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
+ if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
resetTaskIfNeededLocked(r, r);
doShow = topRunningNonDelayedActivityLocked(null) == r;
}
// the root, we may no longer have the task!).
final ArrayList<ActivityRecord> activities = task.mActivities;
final int numActivities = activities.size();
- for (int i = numActivities - 1; i > 0; --i ) {
+ final int rootActivityNdx = task.findEffectiveRootIndex();
+ for (int i = numActivities - 1; i > rootActivityNdx; --i ) {
ActivityRecord target = activities.get(i);
final int flags = target.info.flags;
final ArrayList<ActivityRecord> activities = affinityTask.mActivities;
final int numActivities = activities.size();
- // Do not operate on the root Activity.
- for (int i = numActivities - 1; i > 0; --i) {
+ final int rootActivityNdx = affinityTask.findEffectiveRootIndex();
+
+ // Do not operate on or below the effective root Activity.
+ for (int i = numActivities - 1; i > rootActivityNdx; --i) {
ActivityRecord target = activities.get(i);
final int flags = target.info.flags;
// to run in multiple processes, because this is actually
// part of the framework so doesn't make sense to track as a
// separate apk in the process.
- app.addPackage(r.info.packageName, mService.mProcessStats);
+ app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
+ mService.mProcessStats);
}
realStartActivityLocked(r, app, andResume, checkConfig);
return;
info.activityInfo.applicationInfo.uid, false);
if (app != null && app.thread != null) {
try {
- app.addPackage(info.activityInfo.packageName, mService.mProcessStats);
+ app.addPackage(info.activityInfo.packageName,
+ info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
processCurBroadcastLocked(r, app);
return;
} catch (RemoteException e) {
final int userId; // user of process.
final String processName; // name of the process
// List of packages running in the process
- final ArrayMap<String, ProcessStats.ProcessState> pkgList
- = new ArrayMap<String, ProcessStats.ProcessState>();
+ final ArrayMap<String, ProcessStats.ProcessStateHolder> pkgList
+ = new ArrayMap<String, ProcessStats.ProcessStateHolder>();
IApplicationThread thread; // the actual proc... may be null only if
// 'persistent' is true (in which case we
// are in the process of launching the app)
uid = _uid;
userId = UserHandle.getUserId(_uid);
processName = _processName;
- pkgList.put(_info.packageName, null);
+ pkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.versionCode));
maxAdj = ProcessList.UNKNOWN_ADJ;
curRawAdj = setRawAdj = -100;
curAdj = setAdj = -100;
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();
+ ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
+ if (holder.state != null && holder.state != origBase) {
+ holder.state.makeInactive();
}
- ps = tracker.getProcessStateLocked(pkgList.keyAt(i), info.uid,
+ holder.state = tracker.getProcessStateLocked(pkgList.keyAt(i), info.uid,
info.versionCode, processName);
- if (ps != baseProcessTracker) {
- ps.makeActive();
+ if (holder.state != baseProcessTracker) {
+ holder.state.makeActive();
}
- pkgList.setValueAt(i, ps);
}
}
thread = _thread;
}
baseProcessTracker = null;
for (int i=0; i<pkgList.size(); i++) {
- ProcessStats.ProcessState ps = pkgList.valueAt(i);
- if (ps != null && ps != origBase) {
- ps.makeInactive();
+ ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
+ if (holder.state != null && holder.state != origBase) {
+ holder.state.makeInactive();
}
- pkgList.setValueAt(i, null);
+ holder.state = null;
}
}
}
/*
* Return true if package has been added false if not
*/
- public boolean addPackage(String pkg, ProcessStatsService tracker) {
+ public boolean addPackage(String pkg, int versionCode, ProcessStatsService tracker) {
if (!pkgList.containsKey(pkg)) {
if (baseProcessTracker != null) {
- ProcessStats.ProcessState state = tracker.getProcessStateLocked(
- pkg, info.uid, info.versionCode, processName);
- pkgList.put(pkg, state);
- if (state != baseProcessTracker) {
- state.makeActive();
+ ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
+ versionCode);
+ holder.state = tracker.getProcessStateLocked(
+ pkg, info.uid, versionCode, processName);
+ pkgList.put(pkg, holder);
+ if (holder.state != baseProcessTracker) {
+ holder.state.makeActive();
}
} else {
pkgList.put(pkg, null);
tracker.getMemFactorLocked(), now, pkgList);
if (N != 1) {
for (int i=0; i<N; i++) {
- ProcessStats.ProcessState ps = pkgList.valueAt(i);
- if (ps != null && ps != baseProcessTracker) {
- ps.makeInactive();
+ ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
+ if (holder.state != null && holder.state != baseProcessTracker) {
+ holder.state.makeInactive();
}
}
pkgList.clear();
ProcessStats.ProcessState ps = tracker.getProcessStateLocked(
info.packageName, info.uid, info.versionCode, processName);
- pkgList.put(info.packageName, ps);
+ ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
+ info.versionCode);
+ holder.state = ps;
+ pkgList.put(info.packageName, holder);
if (ps != baseProcessTracker) {
ps.makeActive();
}
}
} else if (N != 1) {
pkgList.clear();
- pkgList.put(info.packageName, null);
+ pkgList.put(info.packageName, new ProcessStats.ProcessStateHolder(info.versionCode));
}
}
return mProcessStats.mMemFactor != ProcessStats.STATE_NOTHING ? mProcessStats.mMemFactor : 0;
}
+ public void addSysMemUsageLocked(long cachedMem, long freeMem, long zramMem, long kernelMem,
+ long nativeMem) {
+ mProcessStats.addSysMemUsage(cachedMem, freeMem, zramMem, kernelMem, nativeMem);
+ }
+
public boolean shouldWriteNowLocked(long now) {
if (now > (mLastWriteTime+WRITE_PERIOD)) {
if (SystemClock.elapsedRealtime()
- > (mProcessStats.mTimePeriodStartRealtime+ProcessStats.COMMIT_PERIOD)) {
+ > (mProcessStats.mTimePeriodStartRealtime+ProcessStats.COMMIT_PERIOD) &&
+ SystemClock.uptimeMillis()
+ > (mProcessStats.mTimePeriodStartUptime+ProcessStats.COMMIT_UPTIME_PERIOD)) {
mCommitPending = true;
}
return true;
if (mPendingWrite == null || !mPendingWriteCommitted) {
mPendingWrite = Parcel.obtain();
mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
+ mProcessStats.mTimePeriodEndUptime = now;
if (commit) {
mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE;
}
mWriteLock.lock();
try {
synchronized (mAm) {
+ long now = SystemClock.uptimeMillis();
mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
- mProcessStats.writeToParcel(current, 0);
+ mProcessStats.mTimePeriodEndUptime = now;
+ mProcessStats.writeToParcel(current, now, 0);
}
if (historic != null) {
ArrayList<String> files = getCommittedFiles(0, false, true);
Parcel current = Parcel.obtain();
long curTime;
synchronized (mAm) {
+ long now = SystemClock.uptimeMillis();
mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
- mProcessStats.writeToParcel(current, 0);
+ mProcessStats.mTimePeriodEndUptime = now;
+ mProcessStats.writeToParcel(current, now, 0);
curTime = mProcessStats.mTimePeriodEndRealtime
- mProcessStats.mTimePeriodStartRealtime;
}
static private void dumpHelp(PrintWriter pw) {
pw.println("Process stats (procstats) dump options:");
pw.println(" [--checkin|-c|--csv] [--csv-screen] [--csv-proc] [--csv-mem]");
- pw.println(" [--details] [--full-details] [--current] [--hours] [--active]");
- pw.println(" [--commit] [--reset] [--clear] [--write] [-h] [<package.name>]");
+ pw.println(" [--details] [--full-details] [--current] [--hours N] [--last N]");
+ pw.println(" [--active] [--commit] [--reset] [--clear] [--write] [-h] [<package.name>]");
pw.println(" --checkin: perform a checkin: print and delete old committed states.");
pw.println(" --c: print only state in checkin format.");
pw.println(" --csv: output data suitable for putting in a spreadsheet.");
pw.println(" --full-details: dump all timing and active state details.");
pw.println(" --current: only dump current state.");
pw.println(" --hours: aggregate over about N last hours.");
+ pw.println(" --last: only show the last committed stats at index N (starting at 1).");
pw.println(" --active: only show currently active processes/services.");
pw.println(" --commit: commit current stats to disk and reset to start new stats.");
pw.println(" --reset: reset current stats, without committing.");
boolean dumpFullDetails = false;
boolean dumpAll = false;
int aggregateHours = 0;
+ int lastIndex = 0;
boolean activeOnly = false;
String reqPackage = null;
boolean csvSepScreenStats = false;
dumpHelp(pw);
return;
}
+ } else if ("--last".equals(arg)) {
+ i++;
+ if (i >= args.length) {
+ pw.println("Error: argument required for --last");
+ dumpHelp(pw);
+ return;
+ }
+ try {
+ lastIndex = Integer.parseInt(args[i]);
+ } catch (NumberFormatException e) {
+ pw.println("Error: --last argument not an int -- " + args[i]);
+ dumpHelp(pw);
+ return;
+ }
} else if ("--active".equals(arg)) {
activeOnly = true;
currentOnly = true;
dumpAggregatedStats(pw, aggregateHours, now, reqPackage, isCompact,
dumpDetails, dumpFullDetails, dumpAll, activeOnly);
return;
+ } else if (lastIndex > 0) {
+ pw.print("LAST STATS AT INDEX "); pw.print(lastIndex); pw.println(":");
+ ArrayList<String> files = getCommittedFiles(0, false, true);
+ if (lastIndex >= files.size()) {
+ pw.print("Only have "); pw.print(files.size()); pw.println(" data sets");
+ return;
+ }
+ AtomicFile file = new AtomicFile(new File(files.get(lastIndex)));
+ ProcessStats processStats = new ProcessStats(false);
+ readLocked(processStats, file);
+ if (processStats.mReadError != null) {
+ if (isCheckin || isCompact) pw.print("err,");
+ pw.print("Failure reading "); pw.print(files.get(lastIndex));
+ pw.print("; "); pw.println(processStats.mReadError);
+ return;
+ }
+ String fileStr = file.getBaseFile().getPath();
+ boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX);
+ if (isCheckin || isCompact) {
+ // Don't really need to lock because we uniquely own this object.
+ processStats.dumpCheckinLocked(pw, reqPackage);
+ } else {
+ pw.print("COMMITTED STATS FROM ");
+ pw.print(processStats.mTimePeriodStartClockStr);
+ if (checkedIn) pw.print(" (checked in)");
+ pw.println(":");
+ if (dumpDetails || dumpFullDetails) {
+ processStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll,
+ activeOnly);
+ if (dumpAll) {
+ pw.print(" mFile="); pw.println(mFile.getBaseFile());
+ }
+ } else {
+ processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
+ }
+ }
+ return;
}
boolean sepNeeded = false;
// Always dump summary here, dumping all details is just too
// much crud.
if (dumpFullDetails) {
- mProcessStats.dumpLocked(pw, reqPackage, now, false, false,
+ processStats.dumpLocked(pw, reqPackage, now, false, false,
activeOnly);
} else {
processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
import android.util.Slog;
private static final String ATTR_ONTOPOFHOME = "on_top_of_home";
private static final String ATTR_LASTDESCRIPTION = "last_description";
private static final String ATTR_LASTTIMEMOVED = "last_time_moved";
+ private static final String ATTR_NEVERRELINQUISH = "never_relinquish_identity";
private static final String TASK_THUMBNAIL_SUFFIX = "_task_thumbnail";
final int taskId; // Unique identifier for this task.
- final String affinity; // The affinity name for this task, or null.
+ String affinity; // The affinity name for this task, or null.
final IVoiceInteractionSession voiceSession; // Voice interaction session driving task
final IVoiceInteractor voiceInteractor; // Associated interactor to provide to app
Intent intent; // The original intent that started the task.
* Display.DEFAULT_DISPLAY. */
boolean mOnTopOfHome = false;
+ /** If original intent did not allow relinquishing task identity, save that information */
+ boolean mNeverRelinquishIdentity = true;
+
final ActivityManagerService mService;
TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) {
mService = service;
taskId = _taskId;
- affinity = info.taskAffinity;
voiceSession = _voiceSession;
voiceInteractor = _voiceInteractor;
setIntent(_intent, info);
String _affinity, ComponentName _realActivity, ComponentName _origActivity,
boolean _rootWasReset, boolean _askedCompatMode, int _taskType, boolean _onTopOfHome,
int _userId, String _lastDescription, ArrayList<ActivityRecord> activities,
- long lastTimeMoved) {
+ long lastTimeMoved, boolean neverRelinquishIdentity) {
mService = service;
taskId = _taskId;
intent = _intent;
lastDescription = _lastDescription;
mActivities = activities;
mLastTimeMoved = lastTimeMoved;
+ mNeverRelinquishIdentity = neverRelinquishIdentity;
}
void touchActiveTime() {
}
void setIntent(Intent _intent, ActivityInfo info) {
+ if (intent == null) {
+ mNeverRelinquishIdentity =
+ (info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) == 0;
+ } else if (mNeverRelinquishIdentity) {
+ return;
+ }
+
+ affinity = info.taskAffinity;
stringName = null;
if (info.targetActivity == null) {
mActivities.remove(newTop);
mActivities.add(newTop);
+ updateEffectiveIntent();
setFrontOfTask();
}
r.mActivityType = taskType;
}
mActivities.add(index, r);
+ updateEffectiveIntent();
if (r.isPersistable()) {
mService.notifyTaskPersisterLocked(this, false);
}
// Was previously in list.
numFullscreen--;
}
+ updateEffectiveIntent();
if (r.isPersistable()) {
mService.notifyTaskPersisterLocked(this, false);
}
// utility activities.
int activityNdx;
final int numActivities = mActivities.size();
+ final boolean relinquish = numActivities == 0 ? false :
+ (mActivities.get(0).info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) != 0;
for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities;
++activityNdx) {
final ActivityRecord r = mActivities.get(activityNdx);
+ if (relinquish && (r.info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) == 0) {
+ // This will be the top activity for determining taskDescription. Pre-inc to
+ // overcome initial decrement below.
+ ++activityNdx;
+ break;
+ }
if (r.intent != null &&
- (r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET)
- != 0) {
+ (r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
break;
}
}
}
}
+ int findEffectiveRootIndex() {
+ int activityNdx;
+ final int topActivityNdx = mActivities.size() - 1;
+ for (activityNdx = 0; activityNdx < topActivityNdx; ++activityNdx) {
+ final ActivityRecord r = mActivities.get(activityNdx);
+ if (r.finishing) {
+ continue;
+ }
+ if ((r.info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) == 0) {
+ break;
+ }
+ }
+ return activityNdx;
+ }
+
+ void updateEffectiveIntent() {
+ final int effectiveRootIndex = findEffectiveRootIndex();
+ final ActivityRecord r = mActivities.get(effectiveRootIndex);
+ setIntent(r.intent, r.info);
+ }
+
void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
Slog.i(TAG, "Saving task=" + this);
out.attribute(null, ATTR_TASKTYPE, String.valueOf(taskType));
out.attribute(null, ATTR_ONTOPOFHOME, String.valueOf(mOnTopOfHome));
out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
+ out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity));
if (lastDescription != null) {
out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString());
}
int userId = 0;
String lastDescription = null;
long lastTimeOnTop = 0;
+ boolean neverRelinquishIdentity = true;
int taskId = -1;
final int outerDepth = in.getDepth();
lastDescription = attrValue;
} else if (ATTR_LASTTIMEMOVED.equals(attrName)) {
lastTimeOnTop = Long.valueOf(attrValue);
+ } else if (ATTR_NEVERRELINQUISH.equals(attrName)) {
+ neverRelinquishIdentity = Boolean.valueOf(attrValue);
} else {
Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
}
final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
affinityIntent, affinity, realActivity, origActivity, rootHasReset,
askedCompatMode, taskType, onTopOfHome, userId, lastDescription, activities,
- lastTimeOnTop);
+ lastTimeOnTop, neverRelinquishIdentity);
for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
}
private static void invokeCallback(IHdmiControlCallback callback, int result) {
+ if (callback == null) {
+ return;
+ }
try {
callback.onComplete(result);
} catch (RemoteException e) {
final void addCecDevice(HdmiCecDeviceInfo info) {
assertRunOnServiceThread();
addDeviceInfo(info);
-
+ mService.invokeDeviceEventListeners(info, true);
// TODO: announce new device detection.
}
*/
final void removeCecDevice(int address) {
assertRunOnServiceThread();
- removeDeviceInfo(address);
+ HdmiCecDeviceInfo info = removeDeviceInfo(address);
mCecMessageCache.flushMessagesFrom(address);
-
- // TODO: announce a device removal.
+ mService.invokeDeviceEventListeners(info, false);
}
/**
import android.util.SparseArray;
import android.util.SparseIntArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.SystemService;
import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
// List of listeners registered by callers that want to get notified of
// hotplug events.
+ @GuardedBy("mLock")
private final ArrayList<IHdmiHotplugEventListener> mHotplugEventListeners = new ArrayList<>();
// List of records for hotplug event listener to handle the the caller killed in action.
+ @GuardedBy("mLock")
private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
new ArrayList<>();
// List of listeners registered by callers that want to get notified of
// device status events.
+ @GuardedBy("mLock")
private final ArrayList<IHdmiDeviceEventListener> mDeviceEventListeners = new ArrayList<>();
// List of records for device event listener to handle the the caller killed in action.
+ @GuardedBy("mLock")
private final ArrayList<DeviceEventListenerRecord> mDeviceEventListenerRecords =
new ArrayList<>();
- // Handler running on service thread. It's used to run a task in service thread.
+ // Handler used to run a task in service thread.
private final Handler mHandler = new Handler();
@Nullable
}
finished.append(deviceType, logicalAddress);
- // Once finish address allocation for all devices, notify
- // it to each device.
+ // Address allocation completed for all devices. Notify each device.
if (deviceTypes.size() == finished.size()) {
notifyAddressAllocated(devices);
}
}
private void addDeviceEventListener(IHdmiDeviceEventListener listener) {
+ DeviceEventListenerRecord record = new DeviceEventListenerRecord(listener);
+ try {
+ listener.asBinder().linkToDeath(record, 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Listener already died");
+ return;
+ }
synchronized (mLock) {
- for (DeviceEventListenerRecord record : mDeviceEventListenerRecords) {
- if (record.mListener.asBinder() == listener.asBinder()) {
- listener.asBinder().unlinkToDeath(record, 0);
- mDeviceEventListenerRecords.remove(record);
- break;
+ mDeviceEventListeners.add(listener);
+ mDeviceEventListenerRecords.add(record);
+ }
+ }
+
+ void invokeDeviceEventListeners(HdmiCecDeviceInfo device, boolean activated) {
+ synchronized (mLock) {
+ for (IHdmiDeviceEventListener listener : mDeviceEventListeners) {
+ try {
+ listener.onStatusChanged(device, activated);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to report device event:" + e);
}
}
- mHotplugEventListeners.remove(listener);
}
}
}
}
- private void announceHotplugEvent(int portNo, boolean connected) {
- HdmiHotplugEvent event = new HdmiHotplugEvent(portNo, connected);
+ private void announceHotplugEvent(int portId, boolean connected) {
+ HdmiHotplugEvent event = new HdmiHotplugEvent(portId, connected);
synchronized (mLock) {
for (IHdmiHotplugEventListener listener : mHotplugEventListeners) {
- invokeHotplugEventListener(listener, event);
+ invokeHotplugEventListenerLocked(listener, event);
}
}
}
- private void invokeHotplugEventListener(IHdmiHotplugEventListener listener,
+ private void invokeHotplugEventListenerLocked(IHdmiHotplugEventListener listener,
HdmiHotplugEvent event) {
try {
listener.onReceived(event);
try {
pp.collectCertificates(pkg, parseFlags);
+ pp.collectManifestDigest(pkg);
} catch (PackageParserException e) {
mLastScanError = e.error;
return false;
try {
pp.collectCertificates(pkg, parseFlags);
+ pp.collectManifestDigest(pkg);
} catch (PackageParserException e) {
res.returnCode = e.error;
return;
} finally {
Binder.restoreCallingIdentity(identity);
}
- return false;
+ // STOPSHIP: Redesign the API around the availability change. For now, the service
+ // will be always available.
+ return true;
}
@Override
public static int GOOD_RSSI_24 = -65;
/** @hide **/
- public static int LOW_RSSI_24 = -75;
+ public static int LOW_RSSI_24 = -77;
/** @hide **/
public static int BAD_RSSI_24 = -87;
/** @hide **/
- public static int GOOD_RSSI_5 = -55;
+ public static int GOOD_RSSI_5 = -60;
/** @hide **/
- public static int LOW_RSSI_5 = -65;
+ public static int LOW_RSSI_5 = -72;
/** @hide **/
- public static int BAD_RSSI_5 = -75;
+ public static int BAD_RSSI_5 = -82;
/** @hide **/
public static int UNWANTED_BLACKLIST_SOFT_BUMP = 4;
* 5GHz band is penalized if the 5GHz RSSI is lower than this threshold **/
public static int G_BAND_PREFERENCE_RSSI_THRESHOLD = -75;
+ /** @hide
+ * Boost given to RSSI on a home network for the purpose of calculating the score
+ * This adds stickiness to home networks, as defined by:
+ * - less than 4 known BSSIDs
+ * - PSK only
+ * - TODO: add a test to verify that all BSSIDs are behind same gateway
+ ***/
+ public static int HOME_NETWORK_RSSI_BOOST = 5;
+
/**
* @hide
* A summary of the RSSI and Band status for that configuration