Merge "Add dumping the state of the print sub-system." into klp-dev
diff --git a/api/current.txt b/api/current.txt
index 5ec1771..78abd53 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -29053,12 +29053,12 @@
}
public class CaptioningManager {
- method public void addCaptioningStateChangeListener(android.view.accessibility.CaptioningManager.CaptioningChangeListener);
+ method public void addCaptioningChangeListener(android.view.accessibility.CaptioningManager.CaptioningChangeListener);
method public final float getFontScale();
method public final java.util.Locale getLocale();
method public android.view.accessibility.CaptioningManager.CaptionStyle getUserStyle();
method public final boolean isEnabled();
- method public void removeCaptioningStateChangeListener(android.view.accessibility.CaptioningManager.CaptioningChangeListener);
+ method public void removeCaptioningChangeListener(android.view.accessibility.CaptioningManager.CaptioningChangeListener);
}
public static final class CaptioningManager.CaptionStyle {
@@ -29072,7 +29072,7 @@
field public final int foregroundColor;
}
- public abstract class CaptioningManager.CaptioningChangeListener {
+ public static abstract class CaptioningManager.CaptioningChangeListener {
ctor public CaptioningManager.CaptioningChangeListener();
method public void onEnabledChanged(boolean);
method public void onFontScaleChanged(float);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 7c40bb1..2d28280 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2106,7 +2106,12 @@
}
// If the target is not exported, then nobody else can get to it.
if (!exported) {
- Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid);
+ /*
+ RuntimeException here = new RuntimeException("here");
+ here.fillInStackTrace();
+ Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid,
+ here);
+ */
return PackageManager.PERMISSION_DENIED;
}
if (permission == null) {
diff --git a/core/java/android/view/accessibility/CaptioningManager.java b/core/java/android/view/accessibility/CaptioningManager.java
index d4c6abe..557239f 100644
--- a/core/java/android/view/accessibility/CaptioningManager.java
+++ b/core/java/android/view/accessibility/CaptioningManager.java
@@ -140,7 +140,7 @@
*
* @param listener the listener to add
*/
- public void addCaptioningStateChangeListener(CaptioningChangeListener listener) {
+ public void addCaptioningChangeListener(CaptioningChangeListener listener) {
synchronized (mListeners) {
if (mListeners.isEmpty()) {
registerObserver(Secure.ACCESSIBILITY_CAPTIONING_ENABLED);
@@ -163,11 +163,11 @@
/**
* Removes a listener previously added using
- * {@link #addCaptioningStateChangeListener}.
+ * {@link #addCaptioningChangeListener}.
*
* @param listener the listener to remove
*/
- public void removeCaptioningStateChangeListener(CaptioningChangeListener listener) {
+ public void removeCaptioningChangeListener(CaptioningChangeListener listener) {
synchronized (mListeners) {
mListeners.remove(listener);
@@ -366,7 +366,7 @@
* Listener for changes in captioning properties, including enabled state
* and user style preferences.
*/
- public abstract class CaptioningChangeListener {
+ public static abstract class CaptioningChangeListener {
/**
* Called when the captioning enabled state changes.
*
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index f449797..009b729 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -30,6 +30,7 @@
import android.media.MediaPlayer.OnInfoListener;
import android.media.Metadata;
import android.media.SubtitleController;
+import android.media.SubtitleTrack.RenderingWidget;
import android.media.WebVttRenderer;
import android.net.Uri;
import android.util.AttributeSet;
@@ -46,7 +47,6 @@
import java.io.IOException;
import java.io.InputStream;
-import java.util.ArrayList;
import java.util.Map;
import java.util.Vector;
@@ -100,14 +100,11 @@
private boolean mCanSeekBack;
private boolean mCanSeekForward;
- /** List of views overlaid on top of the video. */
- private ArrayList<View> mOverlays;
+ /** Subtitle rendering widget overlaid on top of the video. */
+ private RenderingWidget mSubtitleWidget;
- /**
- * Listener for overlay layout changes. Invalidates the video view to ensure
- * that captions are redrawn whenever their layout changes.
- */
- private OnLayoutChangeListener mOverlayLayoutListener;
+ /** Listener for changes to subtitle data, used to redraw when needed. */
+ private RenderingWidget.OnChangedListener mSubtitlesChangedListener;
public VideoView(Context context) {
super(context);
@@ -302,11 +299,10 @@
mMediaPlayer = new MediaPlayer();
// TODO: create SubtitleController in MediaPlayer, but we need
// a context for the subtitle renderers
- SubtitleController controller = new SubtitleController(
- getContext(),
- mMediaPlayer.getMediaTimeProvider(),
- mMediaPlayer);
- controller.registerRenderer(new WebVttRenderer(getContext(), null));
+ final Context context = getContext();
+ final SubtitleController controller = new SubtitleController(
+ context, mMediaPlayer.getMediaTimeProvider(), mMediaPlayer);
+ controller.registerRenderer(new WebVttRenderer(context));
mMediaPlayer.setSubtitleAnchor(controller, this);
if (mAudioSession != 0) {
@@ -792,12 +788,29 @@
}
@Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ if (mSubtitleWidget != null) {
+ mSubtitleWidget.onAttachedToWindow();
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ if (mSubtitleWidget != null) {
+ mSubtitleWidget.onDetachedFromWindow();
+ }
+ }
+
+ @Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- // Layout overlay views, if necessary.
- if (changed && mOverlays != null && !mOverlays.isEmpty()) {
- measureAndLayoutOverlays();
+ if (mSubtitleWidget != null) {
+ measureAndLayoutSubtitleWidget();
}
}
@@ -805,104 +818,65 @@
public void draw(Canvas canvas) {
super.draw(canvas);
- final int count = mOverlays.size();
- for (int i = 0; i < count; i++) {
- final View overlay = mOverlays.get(i);
- overlay.draw(canvas);
+ if (mSubtitleWidget != null) {
+ final int saveCount = canvas.save();
+ canvas.translate(getPaddingLeft(), getPaddingTop());
+ mSubtitleWidget.draw(canvas);
+ canvas.restoreToCount(saveCount);
}
}
/**
- * Adds a view to be overlaid on top of this video view. During layout, the
- * view will be forced to match the bounds, less padding, of the video view.
- * <p>
- * Overlays are drawn in the order they are added. The last added overlay
- * will be drawn on top.
- *
- * @param overlay the view to overlay
- * @see #removeOverlay(View)
- */
- private void addOverlay(View overlay) {
- if (mOverlays == null) {
- mOverlays = new ArrayList<View>(1);
- }
-
- if (mOverlayLayoutListener == null) {
- mOverlayLayoutListener = new OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom,
- int oldLeft, int oldTop, int oldRight, int oldBottom) {
- invalidate();
- }
- };
- }
-
- if (mOverlays.isEmpty()) {
- setWillNotDraw(false);
- }
-
- mOverlays.add(overlay);
- overlay.addOnLayoutChangeListener(mOverlayLayoutListener);
- measureAndLayoutOverlays();
- }
-
- /**
- * Removes a view previously added using {@link #addOverlay}.
- *
- * @param overlay the view to remove
- * @see #addOverlay(View)
- */
- private void removeOverlay(View overlay) {
- if (mOverlays == null) {
- return;
- }
-
- overlay.removeOnLayoutChangeListener(mOverlayLayoutListener);
- mOverlays.remove(overlay);
-
- if (mOverlays.isEmpty()) {
- setWillNotDraw(true);
- }
-
- invalidate();
- }
-
- /**
* Forces a measurement and layout pass for all overlaid views.
*
- * @see #addOverlay(View)
+ * @see #setSubtitleWidget(RenderingWidget)
*/
- private void measureAndLayoutOverlays() {
- final int left = getPaddingLeft();
- final int top = getPaddingTop();
- final int right = getWidth() - left - getPaddingRight();
- final int bottom = getHeight() - top - getPaddingBottom();
- final int widthSpec = MeasureSpec.makeMeasureSpec(right - left, MeasureSpec.EXACTLY);
- final int heightSpec = MeasureSpec.makeMeasureSpec(bottom - top, MeasureSpec.EXACTLY);
+ private void measureAndLayoutSubtitleWidget() {
+ final int width = getWidth() - getPaddingLeft() - getPaddingRight();
+ final int height = getHeight() - getPaddingTop() - getPaddingBottom();
- final int count = mOverlays.size();
- for (int i = 0; i < count; i++) {
- final View overlay = mOverlays.get(i);
- overlay.measure(widthSpec, heightSpec);
- overlay.layout(left, top, right, bottom);
- }
+ mSubtitleWidget.setSize(width, height);
}
/** @hide */
@Override
- public void setSubtitleView(View view) {
- if (mSubtitleView == view) {
+ public void setSubtitleWidget(RenderingWidget subtitleWidget) {
+ if (mSubtitleWidget == subtitleWidget) {
return;
}
- if (mSubtitleView != null) {
- removeOverlay(mSubtitleView);
- }
- mSubtitleView = view;
- if (mSubtitleView != null) {
- addOverlay(mSubtitleView);
- }
- }
+ final boolean attachedToWindow = isAttachedToWindow();
+ if (mSubtitleWidget != null) {
+ if (attachedToWindow) {
+ mSubtitleWidget.onDetachedFromWindow();
+ }
- private View mSubtitleView;
+ mSubtitleWidget.setOnChangedListener(null);
+ }
+
+ mSubtitleWidget = subtitleWidget;
+
+ if (subtitleWidget != null) {
+ if (mSubtitlesChangedListener == null) {
+ mSubtitlesChangedListener = new RenderingWidget.OnChangedListener() {
+ @Override
+ public void onChanged(RenderingWidget renderingWidget) {
+ invalidate();
+ }
+ };
+ }
+
+ setWillNotDraw(false);
+ subtitleWidget.setOnChangedListener(mSubtitlesChangedListener);
+
+ if (attachedToWindow) {
+ subtitleWidget.onAttachedToWindow();
+ requestLayout();
+ }
+ } else {
+ setWillNotDraw(true);
+ }
+
+ invalidate();
+ }
}
diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java
index 16b119a..1f55a4c 100644
--- a/core/java/com/android/internal/app/ProcessStats.java
+++ b/core/java/com/android/internal/app/ProcessStats.java
@@ -165,7 +165,7 @@
static final String CSV_SEP = "\t";
// Current version of the parcel format.
- private static final int PARCEL_VERSION = 9;
+ private static final int PARCEL_VERSION = 11;
// In-memory Parcel magic number, used to detect attempts to unmarshall bad data
private static final int MAGIC = 0x50535453;
@@ -204,6 +204,12 @@
int[] mAddLongTable;
int mAddLongTableSize;
+ // For writing parcels.
+ ArrayMap<String, Integer> mCommonStringToIndex;
+
+ // For reading parcels.
+ ArrayList<String> mIndexToCommonString;
+
public ProcessStats(boolean running) {
mRunning = running;
reset();
@@ -247,7 +253,7 @@
if (DEBUG) Slog.d(TAG, "Adding pkg " + pkgName + " uid " + uid
+ " service " + otherSvc.mName);
ServiceState thisSvc = getServiceStateLocked(pkgName, uid,
- null, otherSvc.mName);
+ otherSvc.mProcessName, otherSvc.mName);
thisSvc.add(otherSvc);
}
}
@@ -959,7 +965,15 @@
for (int ip=procMap.size()-1; ip>=0; ip--) {
SparseArray<ProcessState> uids = procMap.valueAt(ip);
for (int iu=uids.size()-1; iu>=0; iu--) {
- uids.valueAt(iu).resetSafely(now);
+ ProcessState ps = uids.valueAt(iu);
+ if (ps.isInUse()) {
+ uids.valueAt(iu).resetSafely(now);
+ } else {
+ uids.removeAt(iu);
+ }
+ }
+ if (uids.size() <= 0) {
+ procMap.removeAt(ip);
}
}
ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
@@ -968,16 +982,27 @@
for (int iu=uids.size()-1; iu>=0; iu--) {
PackageState pkgState = uids.valueAt(iu);
for (int iproc=pkgState.mProcesses.size()-1; iproc>=0; iproc--) {
- pkgState.mProcesses.valueAt(iproc).resetSafely(now);
+ ProcessState ps = pkgState.mProcesses.valueAt(iproc);
+ if (ps.isInUse()) {
+ pkgState.mProcesses.valueAt(iproc).resetSafely(now);
+ } else {
+ pkgState.mProcesses.removeAt(iproc);
+ }
}
for (int isvc=pkgState.mServices.size()-1; isvc>=0; isvc--) {
ServiceState ss = pkgState.mServices.valueAt(isvc);
- if (ss.isActive()) {
+ if (ss.isInUse()) {
pkgState.mServices.valueAt(isvc).resetSafely(now);
} else {
pkgState.mServices.removeAt(isvc);
}
}
+ if (pkgState.mProcesses.size() <= 0 && pkgState.mServices.size() <= 0) {
+ uids.removeAt(iu);
+ }
+ }
+ if (uids.size() <= 0) {
+ pkgMap.removeAt(ip);
}
}
mStartTime = SystemClock.uptimeMillis();
@@ -1048,6 +1073,75 @@
return table;
}
+ private void writeCompactedLongArray(Parcel out, long[] array) {
+ final int N = array.length;
+ out.writeInt(N);
+ for (int i=0; i<N; i++) {
+ long val = array[i];
+ if (val < 0) {
+ Slog.w(TAG, "Time val negative: " + val);
+ val = 0;
+ }
+ if (val <= Integer.MAX_VALUE) {
+ out.writeInt((int)val);
+ } else {
+ int top = ~((int)((val>>32)&0x7fffffff));
+ int bottom = (int)(val&0xfffffff);
+ out.writeInt(top);
+ out.writeInt(bottom);
+ }
+ }
+ }
+
+ private void readCompactedLongArray(Parcel in, int version, long[] array) {
+ if (version <= 10) {
+ in.readLongArray(array);
+ return;
+ }
+ final int N = in.readInt();
+ if (N != array.length) {
+ throw new RuntimeException("bad array lengths");
+ }
+ for (int i=0; i<N; i++) {
+ int val = in.readInt();
+ if (val >= 0) {
+ array[i] = val;
+ } else {
+ int bottom = in.readInt();
+ array[i] = (((long)~val)<<32) | bottom;
+ }
+ }
+ }
+
+ private void writeCommonString(Parcel out, String name) {
+ Integer index = mCommonStringToIndex.get(name);
+ if (index != null) {
+ out.writeInt(index);
+ return;
+ }
+ index = mCommonStringToIndex.size();
+ mCommonStringToIndex.put(name, index);
+ out.writeInt(~index);
+ out.writeString(name);
+ }
+
+ private String readCommonString(Parcel in, int version) {
+ if (version <= 9) {
+ return in.readString();
+ }
+ int index = in.readInt();
+ if (index >= 0) {
+ return mIndexToCommonString.get(index);
+ }
+ index = ~index;
+ String name = in.readString();
+ while (mIndexToCommonString.size() <= index) {
+ mIndexToCommonString.add(null);
+ }
+ mIndexToCommonString.set(index, name);
+ return name;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -1063,6 +1157,8 @@
out.writeInt(PSS_COUNT);
out.writeInt(LONGS_SIZE);
+ mCommonStringToIndex = new ArrayMap<String, Integer>(mProcesses.mMap.size());
+
// First commit all running times.
ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
final int NPROC = procMap.size();
@@ -1104,7 +1200,7 @@
out.writeInt(mLongs.size());
out.writeInt(mNextLong);
for (int i=0; i<(mLongs.size()-1); i++) {
- out.writeLongArray(mLongs.get(i));
+ writeCompactedLongArray(out, mLongs.get(i));
}
long[] lastLongs = mLongs.get(mLongs.size() - 1);
for (int i=0; i<mNextLong; i++) {
@@ -1116,24 +1212,24 @@
mMemFactorDurations[mMemFactor] += now - mStartTime;
mStartTime = now;
}
- out.writeLongArray(mMemFactorDurations);
+ writeCompactedLongArray(out, mMemFactorDurations);
out.writeInt(NPROC);
for (int ip=0; ip<NPROC; ip++) {
- out.writeString(procMap.keyAt(ip));
+ writeCommonString(out, procMap.keyAt(ip));
SparseArray<ProcessState> uids = procMap.valueAt(ip);
final int NUID = uids.size();
out.writeInt(NUID);
for (int iu=0; iu<NUID; iu++) {
out.writeInt(uids.keyAt(iu));
ProcessState proc = uids.valueAt(iu);
- out.writeString(proc.mPackage);
+ writeCommonString(out, proc.mPackage);
proc.writeToParcel(out, now);
}
}
out.writeInt(NPKG);
for (int ip=0; ip<NPKG; ip++) {
- out.writeString(pkgMap.keyAt(ip));
+ writeCommonString(out, pkgMap.keyAt(ip));
SparseArray<PackageState> uids = pkgMap.valueAt(ip);
final int NUID = uids.size();
out.writeInt(NUID);
@@ -1143,7 +1239,7 @@
final int NPROCS = pkgState.mProcesses.size();
out.writeInt(NPROCS);
for (int iproc=0; iproc<NPROCS; iproc++) {
- out.writeString(pkgState.mProcesses.keyAt(iproc));
+ writeCommonString(out, pkgState.mProcesses.keyAt(iproc));
ProcessState proc = pkgState.mProcesses.valueAt(iproc);
if (proc.mCommonProcess == proc) {
// This is the same as the common process we wrote above.
@@ -1159,10 +1255,13 @@
for (int isvc=0; isvc<NSRVS; isvc++) {
out.writeString(pkgState.mServices.keyAt(isvc));
ServiceState svc = pkgState.mServices.valueAt(isvc);
+ writeCommonString(out, svc.mProcessName);
svc.writeToParcel(out, now);
}
}
}
+
+ mCommonStringToIndex = null;
}
private boolean readCheckedInt(Parcel in, int val, String what) {
@@ -1222,7 +1321,7 @@
return;
}
int version = in.readInt();
- if (version != PARCEL_VERSION && version != 6) {
+ if (version != PARCEL_VERSION) {
mReadError = "bad version: " + version;
return;
}
@@ -1239,14 +1338,14 @@
return;
}
+ mIndexToCommonString = new ArrayList<String>();
+
mTimePeriodStartClock = in.readLong();
buildTimePeriodStartClockStr();
mTimePeriodStartRealtime = in.readLong();
mTimePeriodEndRealtime = in.readLong();
- if (version == PARCEL_VERSION) {
- mRuntime = in.readString();
- mWebView = in.readString();
- }
+ mRuntime = in.readString();
+ mWebView = in.readString();
mFlags = in.readInt();
final int NLONGS = in.readInt();
@@ -1256,7 +1355,7 @@
while (i >= mLongs.size()) {
mLongs.add(new long[LONGS_SIZE]);
}
- in.readLongArray(mLongs.get(i));
+ readCompactedLongArray(in, version, mLongs.get(i));
}
long[] longs = new long[LONGS_SIZE];
mNextLong = NEXTLONG;
@@ -1266,7 +1365,7 @@
}
mLongs.add(longs);
- in.readLongArray(mMemFactorDurations);
+ readCompactedLongArray(in, version, mMemFactorDurations);
int NPROC = in.readInt();
if (NPROC < 0) {
@@ -1275,7 +1374,7 @@
}
while (NPROC > 0) {
NPROC--;
- String procName = in.readString();
+ String procName = readCommonString(in, version);
if (procName == null) {
mReadError = "bad process name";
return;
@@ -1292,7 +1391,7 @@
mReadError = "bad uid: " + uid;
return;
}
- String pkgName = in.readString();
+ String pkgName = readCommonString(in, version);
if (pkgName == null) {
mReadError = "bad process package name";
return;
@@ -1322,7 +1421,7 @@
}
while (NPKG > 0) {
NPKG--;
- String pkgName = in.readString();
+ String pkgName = readCommonString(in, version);
if (pkgName == null) {
mReadError = "bad package name";
return;
@@ -1348,7 +1447,7 @@
}
while (NPROCS > 0) {
NPROCS--;
- String procName = in.readString();
+ String procName = readCommonString(in, version);
if (procName == null) {
mReadError = "bad package process name";
return;
@@ -1400,9 +1499,10 @@
mReadError = "bad package service name";
return;
}
+ String processName = version > 9 ? readCommonString(in, version) : null;
ServiceState serv = hadData ? pkgState.mServices.get(serviceName) : null;
if (serv == null) {
- serv = new ServiceState(this, pkgName, serviceName, null);
+ serv = new ServiceState(this, pkgName, serviceName, processName, null);
}
if (!serv.readFromParcel(in)) {
return;
@@ -1414,6 +1514,8 @@
}
}
+ mIndexToCommonString = null;
+
if (DEBUG) Slog.d(TAG, "Successfully read procstats!");
}
@@ -1555,12 +1657,11 @@
final ProcessStats.PackageState as = getPackageStateLocked(packageName, uid);
ProcessStats.ServiceState ss = as.mServices.get(className);
if (ss != null) {
- ss.makeActive();
return ss;
}
final ProcessStats.ProcessState ps = processName != null
? getProcessStateLocked(packageName, uid, processName) : null;
- ss = new ProcessStats.ServiceState(this, packageName, className, ps);
+ ss = new ProcessStats.ServiceState(this, packageName, className, processName, ps);
as.mServices.put(className, ss);
return ss;
}
@@ -1602,10 +1703,10 @@
ALL_PROC_STATES, now);
dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
ALL_PROC_STATES);
- if (dumpAll) {
- pw.print(" mNumStartedServices=");
- pw.println(proc.mNumStartedServices);
- }
+ pw.print(" mActive="); pw.println(proc.mActive);
+ pw.print(" mNumActiveServices="); pw.print(proc.mNumActiveServices);
+ pw.print(" mNumStartedServices=");
+ pw.println(proc.mNumStartedServices);
}
} else {
ArrayList<ProcessState> procs = new ArrayList<ProcessState>();
@@ -1624,6 +1725,9 @@
pw.print(pkgState.mServices.keyAt(isvc));
pw.println(":");
ServiceState svc = pkgState.mServices.valueAt(isvc);
+ dumpServiceStats(pw, " ", " ", " ", "Running", svc,
+ svc.mRunCount, ServiceState.SERVICE_RUN, svc.mRunState,
+ svc.mRunStartTime, now, totalTime, dumpAll);
dumpServiceStats(pw, " ", " ", " ", "Started", svc,
svc.mStartedCount, ServiceState.SERVICE_STARTED, svc.mStartedState,
svc.mStartedStartTime, now, totalTime, dumpAll);
@@ -1633,6 +1737,9 @@
dumpServiceStats(pw, " ", " ", " ", "Executing", svc,
svc.mExecCount, ServiceState.SERVICE_EXEC, svc.mExecState,
svc.mExecStartTime, now, totalTime, dumpAll);
+ if (dumpAll) {
+ pw.print(" mActive="); pw.println(svc.mActive);
+ }
}
}
}
@@ -1663,6 +1770,12 @@
ALL_PROC_STATES, now);
dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
ALL_PROC_STATES);
+ if (dumpAll) {
+ pw.print(" mActive="); pw.println(proc.mActive);
+ pw.print(" mNumActiveServices="); pw.print(proc.mNumActiveServices);
+ pw.print(" mNumStartedServices=");
+ pw.println(proc.mNumStartedServices);
+ }
}
}
@@ -1929,6 +2042,9 @@
String serviceName = collapseString(pkgName,
pkgState.mServices.keyAt(isvc));
ServiceState svc = pkgState.mServices.valueAt(isvc);
+ dumpServiceTimeCheckin(pw, "pkgsvc-run", pkgName, uid, serviceName,
+ svc, ServiceState.SERVICE_RUN, svc.mRunCount,
+ svc.mRunState, svc.mRunStartTime, now);
dumpServiceTimeCheckin(pw, "pkgsvc-start", pkgName, uid, serviceName,
svc, ServiceState.SERVICE_STARTED, svc.mStartedCount,
svc.mStartedState, svc.mStartedStartTime, now);
@@ -2003,6 +2119,8 @@
int[] mPssTable;
int mPssTableSize;
+ boolean mActive;
+ int mNumActiveServices;
int mNumStartedServices;
int mNumExcessiveWake;
@@ -2072,6 +2190,7 @@
}
pnew.mNumExcessiveWake = mNumExcessiveWake;
pnew.mNumExcessiveCpu = mNumExcessiveCpu;
+ pnew.mActive = mActive;
pnew.mNumStartedServices = mNumStartedServices;
return pnew;
}
@@ -2151,6 +2270,18 @@
return true;
}
+ public void makeActive() {
+ mActive = true;
+ }
+
+ public void makeInactive() {
+ mActive = false;
+ }
+
+ public boolean isInUse() {
+ return mActive || mNumActiveServices > 0 || mNumStartedServices > 0;
+ }
+
/**
* Update the current state of the given list of processes.
*
@@ -2219,6 +2350,24 @@
longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += dur;
}
+ void incActiveServices() {
+ if (mCommonProcess != this) {
+ mCommonProcess.incActiveServices();
+ }
+ mNumActiveServices++;
+ }
+
+ void decActiveServices() {
+ if (mCommonProcess != this) {
+ mCommonProcess.decActiveServices();
+ }
+ mNumActiveServices--;
+ if (mNumActiveServices < 0) {
+ throw new IllegalStateException("Proc active services underrun: pkg="
+ + mPackage + " uid=" + mUid + " name=" + mName);
+ }
+ }
+
void incStartedServices(int memFactor, long now) {
if (mCommonProcess != this) {
mCommonProcess.incStartedServices(memFactor, now);
@@ -2406,18 +2555,24 @@
final ProcessStats mStats;
public final String mPackage;
public final String mName;
+ public final String mProcessName;
ProcessState mProc;
- int mActive = 1;
+ int mActive = 0;
- public static final int SERVICE_STARTED = 0;
- public static final int SERVICE_BOUND = 1;
- public static final int SERVICE_EXEC = 2;
- static final int SERVICE_COUNT = 3;
+ public static final int SERVICE_RUN = 0;
+ public static final int SERVICE_STARTED = 1;
+ public static final int SERVICE_BOUND = 2;
+ public static final int SERVICE_EXEC = 3;
+ static final int SERVICE_COUNT = 4;
int[] mDurationsTable;
int mDurationsTableSize;
+ int mRunCount;
+ public int mRunState = STATE_NOTHING;
+ long mRunStartTime;
+
int mStartedCount;
public int mStartedState = STATE_NOTHING;
long mStartedStartTime;
@@ -2430,14 +2585,19 @@
public int mExecState = STATE_NOTHING;
long mExecStartTime;
- public ServiceState(ProcessStats processStats, String pkg, String name, ProcessState proc) {
+ public ServiceState(ProcessStats processStats, String pkg, String name,
+ String processName, ProcessState proc) {
mStats = processStats;
mPackage = pkg;
mName = name;
+ mProcessName = processName;
mProc = proc;
}
public void makeActive() {
+ if (mActive == 0) {
+ mProc.incActiveServices();
+ }
mActive++;
}
@@ -2448,9 +2608,12 @@
Slog.i(TAG, "Making " + this + " inactive", here);
*/
mActive--;
+ if (mActive == 0) {
+ mProc.decActiveServices();
+ }
}
- public boolean isActive() {
+ public boolean isInUse() {
return mActive > 0;
}
@@ -2460,6 +2623,7 @@
int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
addStateTime(state, other.mStats.getLong(ent, 0));
}
+ mRunCount += other.mRunCount;
mStartedCount += other.mStartedCount;
mBoundCount += other.mBoundCount;
mExecCount += other.mExecCount;
@@ -2468,6 +2632,7 @@
void resetSafely(long now) {
mDurationsTable = null;
mDurationsTableSize = 0;
+ mRunCount = mRunState != STATE_NOTHING ? 1 : 0;
mStartedCount = mStartedState != STATE_NOTHING ? 1 : 0;
mBoundCount = mBoundState != STATE_NOTHING ? 1 : 0;
mExecCount = mExecState != STATE_NOTHING ? 1 : 0;
@@ -2481,6 +2646,7 @@
+ printLongOffset(mDurationsTable[i]));
out.writeInt(mDurationsTable[i]);
}
+ out.writeInt(mRunCount);
out.writeInt(mStartedCount);
out.writeInt(mBoundCount);
out.writeInt(mExecCount);
@@ -2493,6 +2659,7 @@
return false;
}
mDurationsTableSize = mDurationsTable != null ? mDurationsTable.length : 0;
+ mRunCount = in.readInt();
mStartedCount = in.readInt();
mBoundCount = in.readInt();
mExecCount = in.readInt();
@@ -2518,6 +2685,10 @@
}
void commitStateTime(long now) {
+ if (mRunState != STATE_NOTHING) {
+ addStateTime(SERVICE_RUN + (mRunState*SERVICE_COUNT), now - mRunStartTime);
+ mRunStartTime = now;
+ }
if (mStartedState != STATE_NOTHING) {
addStateTime(SERVICE_STARTED + (mStartedState*SERVICE_COUNT),
now - mStartedStartTime);
@@ -2533,6 +2704,21 @@
}
}
+ private void updateRunning(int memFactor, long now) {
+ final int state = (mStartedState != STATE_NOTHING || mBoundState != STATE_NOTHING
+ || mExecState != STATE_NOTHING) ? memFactor : STATE_NOTHING;
+ if (mRunState != state) {
+ if (mRunState != STATE_NOTHING) {
+ addStateTime(SERVICE_RUN + (mRunState*SERVICE_COUNT),
+ now - mRunStartTime);
+ } else if (state != STATE_NOTHING) {
+ mRunCount++;
+ }
+ mRunState = state;
+ mRunStartTime = now;
+ }
+ }
+
public void setStarted(boolean started, int memFactor, long now) {
if (mActive <= 0) {
throw new IllegalStateException("Service " + this + " has mActive=" + mActive);
@@ -2556,6 +2742,7 @@
mProc.decStartedServices(memFactor, now);
}
}
+ updateRunning(memFactor, now);
}
}
@@ -2573,6 +2760,7 @@
}
mBoundState = state;
mBoundStartTime = now;
+ updateRunning(memFactor, now);
}
}
@@ -2589,6 +2777,7 @@
}
mExecState = state;
mExecStartTime = now;
+ updateRunning(memFactor, now);
}
}
diff --git a/core/java/com/android/internal/widget/SubtitleView.java b/core/java/com/android/internal/widget/SubtitleView.java
index 13101512..356401c 100644
--- a/core/java/com/android/internal/widget/SubtitleView.java
+++ b/core/java/com/android/internal/widget/SubtitleView.java
@@ -74,6 +74,10 @@
private float mSpacingAdd = 0;
private int mInnerPaddingX = 0;
+ public SubtitleView(Context context) {
+ this(context, null);
+ }
+
public SubtitleView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
diff --git a/media/java/android/media/SubtitleController.java b/media/java/android/media/SubtitleController.java
index 2cf1b2d..e83c5ba 100644
--- a/media/java/android/media/SubtitleController.java
+++ b/media/java/android/media/SubtitleController.java
@@ -20,8 +20,7 @@
import java.util.Vector;
import android.content.Context;
-import android.media.MediaPlayer.OnSubtitleDataListener;
-import android.view.View;
+import android.media.SubtitleTrack.RenderingWidget;
import android.view.accessibility.CaptioningManager;
/**
@@ -32,7 +31,6 @@
* @hide
*/
public class SubtitleController {
- private Context mContext;
private MediaTimeProvider mTimeProvider;
private Vector<Renderer> mRenderers;
private Vector<SubtitleTrack> mTracks;
@@ -50,7 +48,6 @@
Context context,
MediaTimeProvider timeProvider,
Listener listener) {
- mContext = context;
mTimeProvider = timeProvider;
mListener = listener;
@@ -79,11 +76,11 @@
return mSelectedTrack;
}
- private View getSubtitleView() {
+ private RenderingWidget getRenderingWidget() {
if (mSelectedTrack == null) {
return null;
}
- return mSelectedTrack.getView();
+ return mSelectedTrack.getRenderingWidget();
}
/**
@@ -110,7 +107,7 @@
}
mSelectedTrack = track;
- mAnchor.setSubtitleView(getSubtitleView());
+ mAnchor.setSubtitleWidget(getRenderingWidget());
if (mSelectedTrack != null) {
mSelectedTrack.setTimeProvider(mTimeProvider);
@@ -268,17 +265,16 @@
}
/**
- * Subtitle anchor, an object that is able to display a subtitle view,
+ * Subtitle anchor, an object that is able to display a subtitle renderer,
* e.g. a VideoView.
*/
public interface Anchor {
/**
- * Anchor should set the subtitle view to the supplied view,
- * or none, if the supplied view is null.
- *
- * @param view subtitle view, or null
+ * Anchor should use the supplied subtitle rendering widget, or
+ * none if it is null.
+ * @hide
*/
- public void setSubtitleView(View view);
+ public void setSubtitleWidget(RenderingWidget subtitleWidget);
}
private Anchor mAnchor;
@@ -290,11 +286,11 @@
}
if (mAnchor != null) {
- mAnchor.setSubtitleView(null);
+ mAnchor.setSubtitleWidget(null);
}
mAnchor = anchor;
if (mAnchor != null) {
- mAnchor.setSubtitleView(getSubtitleView());
+ mAnchor.setSubtitleWidget(getRenderingWidget());
}
}
diff --git a/media/java/android/media/SubtitleTrack.java b/media/java/android/media/SubtitleTrack.java
index 09fb3f2..cb689af 100644
--- a/media/java/android/media/SubtitleTrack.java
+++ b/media/java/android/media/SubtitleTrack.java
@@ -16,11 +16,11 @@
package android.media;
+import android.graphics.Canvas;
import android.os.Handler;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.Pair;
-import android.view.View;
import java.util.Iterator;
import java.util.NoSuchElementException;
@@ -98,16 +98,16 @@
public abstract void onData(String data, boolean eos, long runID);
/**
- * Called when adding the subtitle rendering view to the view hierarchy, as
- * well as when showing or hiding the subtitle track, or when the video
+ * Called when adding the subtitle rendering widget to the view hierarchy,
+ * as well as when showing or hiding the subtitle track, or when the video
* surface position has changed.
*
- * @return the view object that displays this subtitle track. For most
- * renderers there should be a single shared view instance that is used
- * for all tracks supported by that renderer, as at most one subtitle
- * track is visible at one time.
+ * @return the widget that renders this subtitle track. For most renderers
+ * there should be a single shared instance that is used for all
+ * tracks supported by that renderer, as at most one subtitle track
+ * is visible at one time.
*/
- public abstract View getView();
+ public abstract RenderingWidget getRenderingWidget();
/**
* Called when the active cues have changed, and the contents of the subtitle
@@ -268,7 +268,7 @@
}
mVisible = true;
- getView().setVisibility(View.VISIBLE);
+ getRenderingWidget().setVisible(true);
if (mTimeProvider != null) {
mTimeProvider.scheduleUpdate(this);
}
@@ -283,7 +283,7 @@
if (mTimeProvider != null) {
mTimeProvider.cancelNotifications(this);
}
- getView().setVisibility(View.INVISIBLE);
+ getRenderingWidget().setVisible(false);
mVisible = false;
}
@@ -645,4 +645,61 @@
}
}
}
+
+ /**
+ * Interface for rendering subtitles onto a Canvas.
+ */
+ public interface RenderingWidget {
+ /**
+ * Sets the widget's callback, which is used to send updates when the
+ * rendered data has changed.
+ *
+ * @param callback update callback
+ */
+ public void setOnChangedListener(OnChangedListener callback);
+
+ /**
+ * Sets the widget's size.
+ *
+ * @param width width in pixels
+ * @param height height in pixels
+ */
+ public void setSize(int width, int height);
+
+ /**
+ * Sets whether the widget should draw subtitles.
+ *
+ * @param visible true if subtitles should be drawn, false otherwise
+ */
+ public void setVisible(boolean visible);
+
+ /**
+ * Renders subtitles onto a {@link Canvas}.
+ *
+ * @param c canvas on which to render subtitles
+ */
+ public void draw(Canvas c);
+
+ /**
+ * Called when the widget is attached to a window.
+ */
+ public void onAttachedToWindow();
+
+ /**
+ * Called when the widget is detached from a window.
+ */
+ public void onDetachedFromWindow();
+
+ /**
+ * Callback used to send updates about changes to rendering data.
+ */
+ public interface OnChangedListener {
+ /**
+ * Called when the rendering data has changed.
+ *
+ * @param renderingWidget the widget whose data has changed
+ */
+ public void onChanged(RenderingWidget renderingWidget);
+ }
+ }
}
diff --git a/media/java/android/media/WebVttRenderer.java b/media/java/android/media/WebVttRenderer.java
index 527c57f..74773a8 100644
--- a/media/java/android/media/WebVttRenderer.java
+++ b/media/java/android/media/WebVttRenderer.java
@@ -1,12 +1,37 @@
+/*
+ * 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 android.media;
import android.content.Context;
+import android.text.SpannableStringBuilder;
+import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.Gravity;
import android.view.View;
-import android.view.ViewGroup.LayoutParams;
-import android.widget.TextView;
+import android.view.ViewGroup;
+import android.view.accessibility.CaptioningManager;
+import android.view.accessibility.CaptioningManager.CaptionStyle;
+import android.view.accessibility.CaptioningManager.CaptioningChangeListener;
+import android.widget.LinearLayout;
+import com.android.internal.widget.SubtitleView;
+
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@@ -14,10 +39,12 @@
/** @hide */
public class WebVttRenderer extends SubtitleController.Renderer {
- private TextView mMyTextView;
+ private final Context mContext;
- public WebVttRenderer(Context context, AttributeSet attrs) {
- mMyTextView = new WebVttView(context, attrs);
+ private WebVttRenderingWidget mRenderingWidget;
+
+ public WebVttRenderer(Context context) {
+ mContext = context;
}
@Override
@@ -30,19 +57,11 @@
@Override
public SubtitleTrack createTrack(MediaFormat format) {
- return new WebVttTrack(format, mMyTextView);
- }
-}
+ if (mRenderingWidget == null) {
+ mRenderingWidget = new WebVttRenderingWidget(mContext);
+ }
-/** @hide */
-class WebVttView extends TextView {
- public WebVttView(Context context, AttributeSet attrs) {
- super(context, attrs);
- setTextColor(0xffffff00);
- setTextSize(46);
- setTextAlignment(TextView.TEXT_ALIGNMENT_CENTER);
- setLayoutParams(new LayoutParams(
- LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
+ return new WebVttTrack(mRenderingWidget, format);
}
}
@@ -954,26 +973,26 @@
class WebVttTrack extends SubtitleTrack implements WebVttCueListener {
private static final String TAG = "WebVttTrack";
- private final TextView mTextView;
-
private final WebVttParser mParser = new WebVttParser(this);
private final UnstyledTextExtractor mExtractor =
new UnstyledTextExtractor();
private final Tokenizer mTokenizer = new Tokenizer(mExtractor);
private final Vector<Long> mTimestamps = new Vector<Long>();
+ private final WebVttRenderingWidget mRenderingWidget;
private final Map<String, TextTrackRegion> mRegions =
new HashMap<String, TextTrackRegion>();
private Long mCurrentRunID;
- WebVttTrack(MediaFormat format, TextView textView) {
+ WebVttTrack(WebVttRenderingWidget renderingWidget, MediaFormat format) {
super(format);
- mTextView = textView;
+
+ mRenderingWidget = renderingWidget;
}
@Override
- public View getView() {
- return mTextView;
+ public WebVttRenderingWidget getRenderingWidget() {
+ return mRenderingWidget;
}
@Override
@@ -1051,6 +1070,7 @@
}
}
+ @Override
public void updateView(Vector<SubtitleTrack.Cue> activeCues) {
if (!mVisible) {
// don't keep the state if we are not visible
@@ -1066,29 +1086,737 @@
Log.d(TAG, "at (illegal state) the active cues are:");
}
}
- StringBuilder text = new StringBuilder();
- StringBuilder lineBuilder = new StringBuilder();
- for (Cue o: activeCues) {
- TextTrackCue cue = (TextTrackCue)o;
- if (DEBUG) Log.d(TAG, cue.toString());
- for (TextTrackCueSpan[] line: cue.mLines) {
- for (TextTrackCueSpan span: line) {
- if (!span.mEnabled) {
- continue;
- }
- lineBuilder.append(span.mText);
+
+ mRenderingWidget.setActiveCues(activeCues);
+ }
+}
+
+/**
+ * Widget capable of rendering WebVTT captions.
+ *
+ * @hide
+ */
+class WebVttRenderingWidget extends ViewGroup implements SubtitleTrack.RenderingWidget {
+ private static final boolean DEBUG = false;
+ private static final int DEBUG_REGION_BACKGROUND = 0x800000FF;
+ private static final int DEBUG_CUE_BACKGROUND = 0x80FF0000;
+
+ /** WebVtt specifies line height as 5.3% of the viewport height. */
+ private static final float LINE_HEIGHT_RATIO = 0.0533f;
+
+ /** Map of active regions, used to determine enter/exit. */
+ private final ArrayMap<TextTrackRegion, RegionLayout> mRegionBoxes =
+ new ArrayMap<TextTrackRegion, RegionLayout>();
+
+ /** Map of active cues, used to determine enter/exit. */
+ private final ArrayMap<TextTrackCue, CueLayout> mCueBoxes =
+ new ArrayMap<TextTrackCue, CueLayout>();
+
+ /** Captioning manager, used to obtain and track caption properties. */
+ private final CaptioningManager mManager;
+
+ /** Callback for rendering changes. */
+ private OnChangedListener mListener;
+
+ /** Current caption style. */
+ private CaptionStyle mCaptionStyle;
+
+ /** Current font size, computed from font scaling factor and height. */
+ private float mFontSize;
+
+ /** Whether a caption style change listener is registered. */
+ private boolean mHasChangeListener;
+
+ public WebVttRenderingWidget(Context context) {
+ this(context, null);
+ }
+
+ public WebVttRenderingWidget(Context context, AttributeSet attrs) {
+ this(context, null, 0);
+ }
+
+ public WebVttRenderingWidget(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ // Cannot render text over video when layer type is hardware.
+ setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+
+ mManager = (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE);
+ mCaptionStyle = mManager.getUserStyle();
+ mFontSize = mManager.getFontScale() * getHeight() * LINE_HEIGHT_RATIO;
+ }
+
+ @Override
+ public void setSize(int width, int height) {
+ final int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
+ final int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
+
+ measure(widthSpec, heightSpec);
+ layout(0, 0, width, height);
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ manageChangeListener();
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ manageChangeListener();
+ }
+
+ @Override
+ public void setOnChangedListener(OnChangedListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void setVisible(boolean visible) {
+ if (visible) {
+ setVisibility(View.VISIBLE);
+ } else {
+ setVisibility(View.GONE);
+ }
+
+ manageChangeListener();
+ }
+
+ /**
+ * Manages whether this renderer is listening for caption style changes.
+ */
+ private void manageChangeListener() {
+ final boolean needsListener = isAttachedToWindow() && getVisibility() == View.VISIBLE;
+ if (mHasChangeListener != needsListener) {
+ mHasChangeListener = needsListener;
+
+ if (needsListener) {
+ mManager.addCaptioningChangeListener(mCaptioningListener);
+
+ final CaptionStyle captionStyle = mManager.getUserStyle();
+ final float fontSize = mManager.getFontScale() * getHeight() * LINE_HEIGHT_RATIO;
+ setCaptionStyle(captionStyle, fontSize);
+ } else {
+ mManager.removeCaptioningChangeListener(mCaptioningListener);
+ }
+ }
+ }
+
+ public void setActiveCues(Vector<SubtitleTrack.Cue> activeCues) {
+ final Context context = getContext();
+ final CaptionStyle captionStyle = mCaptionStyle;
+ final float fontSize = mFontSize;
+
+ prepForPrune();
+
+ // Ensure we have all necessary cue and region boxes.
+ final int count = activeCues.size();
+ for (int i = 0; i < count; i++) {
+ final TextTrackCue cue = (TextTrackCue) activeCues.get(i);
+ final TextTrackRegion region = cue.mRegion;
+ if (region != null) {
+ RegionLayout regionBox = mRegionBoxes.get(region);
+ if (regionBox == null) {
+ regionBox = new RegionLayout(context, region, captionStyle, fontSize);
+ mRegionBoxes.put(region, regionBox);
+ addView(regionBox, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
- if (lineBuilder.length() > 0) {
- text.append(lineBuilder.toString()).append("\n");
- lineBuilder.delete(0, lineBuilder.length());
+ regionBox.put(cue);
+ } else {
+ CueLayout cueBox = mCueBoxes.get(cue);
+ if (cueBox == null) {
+ cueBox = new CueLayout(context, cue, captionStyle, fontSize);
+ mCueBoxes.put(cue, cueBox);
+ addView(cueBox, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+ }
+ cueBox.update();
+ cueBox.setOrder(i);
+ }
+ }
+
+ prune();
+
+ // Force measurement and layout.
+ final int width = getWidth();
+ final int height = getHeight();
+ setSize(width, height);
+
+ if (mListener != null) {
+ mListener.onChanged(this);
+ }
+ }
+
+ private void setCaptionStyle(CaptionStyle captionStyle, float fontSize) {
+ mCaptionStyle = captionStyle;
+ mFontSize = fontSize;
+
+ final int cueCount = mCueBoxes.size();
+ for (int i = 0; i < cueCount; i++) {
+ final CueLayout cueBox = mCueBoxes.valueAt(i);
+ cueBox.setCaptionStyle(captionStyle, fontSize);
+ }
+
+ final int regionCount = mRegionBoxes.size();
+ for (int i = 0; i < regionCount; i++) {
+ final RegionLayout regionBox = mRegionBoxes.valueAt(i);
+ regionBox.setCaptionStyle(captionStyle, fontSize);
+ }
+ }
+
+ /**
+ * Remove inactive cues and regions.
+ */
+ private void prune() {
+ int regionCount = mRegionBoxes.size();
+ for (int i = 0; i < regionCount; i++) {
+ final RegionLayout regionBox = mRegionBoxes.valueAt(i);
+ if (regionBox.prune()) {
+ removeView(regionBox);
+ mRegionBoxes.removeAt(i);
+ regionCount--;
+ i--;
+ }
+ }
+
+ int cueCount = mCueBoxes.size();
+ for (int i = 0; i < cueCount; i++) {
+ final CueLayout cueBox = mCueBoxes.valueAt(i);
+ if (!cueBox.isActive()) {
+ removeView(cueBox);
+ mCueBoxes.removeAt(i);
+ cueCount--;
+ i--;
+ }
+ }
+ }
+
+ /**
+ * Reset active cues and regions.
+ */
+ private void prepForPrune() {
+ final int regionCount = mRegionBoxes.size();
+ for (int i = 0; i < regionCount; i++) {
+ final RegionLayout regionBox = mRegionBoxes.valueAt(i);
+ regionBox.prepForPrune();
+ }
+
+ final int cueCount = mCueBoxes.size();
+ for (int i = 0; i < cueCount; i++) {
+ final CueLayout cueBox = mCueBoxes.valueAt(i);
+ cueBox.prepForPrune();
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ final int regionCount = mRegionBoxes.size();
+ for (int i = 0; i < regionCount; i++) {
+ final RegionLayout regionBox = mRegionBoxes.valueAt(i);
+ regionBox.measureForParent(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ final int cueCount = mCueBoxes.size();
+ for (int i = 0; i < cueCount; i++) {
+ final CueLayout cueBox = mCueBoxes.valueAt(i);
+ cueBox.measureForParent(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ final int viewportWidth = r - l;
+ final int viewportHeight = b - t;
+
+ setCaptionStyle(mCaptionStyle,
+ mManager.getFontScale() * LINE_HEIGHT_RATIO * viewportHeight);
+
+ final int regionCount = mRegionBoxes.size();
+ for (int i = 0; i < regionCount; i++) {
+ final RegionLayout regionBox = mRegionBoxes.valueAt(i);
+ layoutRegion(viewportWidth, viewportHeight, regionBox);
+ }
+
+ final int cueCount = mCueBoxes.size();
+ for (int i = 0; i < cueCount; i++) {
+ final CueLayout cueBox = mCueBoxes.valueAt(i);
+ layoutCue(viewportWidth, viewportHeight, cueBox);
+ }
+ }
+
+ /**
+ * Lays out a region within the viewport. The region handles layout for
+ * contained cues.
+ */
+ private void layoutRegion(
+ int viewportWidth, int viewportHeight,
+ RegionLayout regionBox) {
+ final TextTrackRegion region = regionBox.getRegion();
+ final int regionHeight = regionBox.getMeasuredHeight();
+ final int regionWidth = regionBox.getMeasuredWidth();
+
+ // TODO: Account for region anchor point.
+ final float x = region.mViewportAnchorPointX;
+ final float y = region.mViewportAnchorPointY;
+ final int left = (int) (x * (viewportWidth - regionWidth) / 100);
+ final int top = (int) (y * (viewportHeight - regionHeight) / 100);
+
+ regionBox.layout(left, top, left + regionWidth, top + regionHeight);
+ }
+
+ /**
+ * Lays out a cue within the viewport.
+ */
+ private void layoutCue(
+ int viewportWidth, int viewportHeight, CueLayout cueBox) {
+ final TextTrackCue cue = cueBox.getCue();
+ final int direction = getLayoutDirection();
+ final int absAlignment = resolveCueAlignment(direction, cue.mAlignment);
+ final boolean cueSnapToLines = cue.mSnapToLines;
+
+ int size = 100 * cueBox.getMeasuredWidth() / viewportWidth;
+
+ // Determine raw x-position.
+ int xPosition;
+ switch (absAlignment) {
+ case TextTrackCue.ALIGNMENT_LEFT:
+ xPosition = cue.mTextPosition;
+ break;
+ case TextTrackCue.ALIGNMENT_RIGHT:
+ xPosition = cue.mTextPosition - size;
+ break;
+ case TextTrackCue.ALIGNMENT_MIDDLE:
+ default:
+ xPosition = cue.mTextPosition - size / 2;
+ break;
+ }
+
+ // Adjust x-position for layout.
+ if (direction == LAYOUT_DIRECTION_RTL) {
+ xPosition = 100 - xPosition;
+ }
+
+ // If the text track cue snap-to-lines flag is set, adjust
+ // x-position and size for padding. This is equivalent to placing the
+ // cue within the title-safe area.
+ if (cueSnapToLines) {
+ final int paddingLeft = 100 * getPaddingLeft() / viewportWidth;
+ final int paddingRight = 100 * getPaddingRight() / viewportWidth;
+ if (xPosition < paddingLeft && xPosition + size > paddingLeft) {
+ xPosition += paddingLeft;
+ size -= paddingLeft;
+ }
+ final float rightEdge = 100 - paddingRight;
+ if (xPosition < rightEdge && xPosition + size > rightEdge) {
+ size -= paddingRight;
+ }
+ }
+
+ // Compute absolute left position and width.
+ final int left = xPosition * viewportWidth / 100;
+ final int width = size * viewportWidth / 100;
+
+ // Determine initial y-position.
+ final int yPosition = calculateLinePosition(cueBox);
+
+ // Compute absolute final top position and height.
+ final int height = cueBox.getMeasuredHeight();
+ final int top;
+ if (yPosition < 0) {
+ // TODO: This needs to use the actual height of prior boxes.
+ top = viewportHeight + yPosition * height;
+ } else {
+ top = yPosition * (viewportHeight - height) / 100;
+ }
+
+ // Layout cue in final position.
+ cueBox.layout(left, top, left + width, top + height);
+ }
+
+ /**
+ * Calculates the line position for a cue.
+ * <p>
+ * If the resulting position is negative, it represents a bottom-aligned
+ * position relative to the number of active cues. Otherwise, it represents
+ * a percentage [0-100] of the viewport height.
+ */
+ private int calculateLinePosition(CueLayout cueBox) {
+ final TextTrackCue cue = cueBox.getCue();
+ final Integer linePosition = cue.mLinePosition;
+ final boolean snapToLines = cue.mSnapToLines;
+ final boolean autoPosition = (linePosition == null);
+
+ if (!snapToLines && !autoPosition && (linePosition < 0 || linePosition > 100)) {
+ // Invalid line position defaults to 100.
+ return 100;
+ } else if (!autoPosition) {
+ // Use the valid, supplied line position.
+ return linePosition;
+ } else if (!snapToLines) {
+ // Automatic, non-snapped line position defaults to 100.
+ return 100;
+ } else {
+ // Automatic snapped line position uses active cue order.
+ return -(cueBox.mOrder + 1);
+ }
+ }
+
+ /**
+ * Resolves cue alignment according to the specified layout direction.
+ */
+ private static int resolveCueAlignment(int layoutDirection, int alignment) {
+ switch (alignment) {
+ case TextTrackCue.ALIGNMENT_START:
+ return layoutDirection == View.LAYOUT_DIRECTION_LTR ?
+ TextTrackCue.ALIGNMENT_LEFT : TextTrackCue.ALIGNMENT_RIGHT;
+ case TextTrackCue.ALIGNMENT_END:
+ return layoutDirection == View.LAYOUT_DIRECTION_LTR ?
+ TextTrackCue.ALIGNMENT_RIGHT : TextTrackCue.ALIGNMENT_LEFT;
+ }
+ return alignment;
+ }
+
+ private final CaptioningChangeListener mCaptioningListener = new CaptioningChangeListener() {
+ @Override
+ public void onFontScaleChanged(float fontScale) {
+ final float fontSize = fontScale * getHeight() * LINE_HEIGHT_RATIO;
+ setCaptionStyle(mCaptionStyle, fontSize);
+ }
+
+ @Override
+ public void onUserStyleChanged(CaptionStyle userStyle) {
+ setCaptionStyle(userStyle, mFontSize);
+ }
+ };
+
+ /**
+ * A text track region represents a portion of the video viewport and
+ * provides a rendering area for text track cues.
+ */
+ private static class RegionLayout extends LinearLayout {
+ private final ArrayList<CueLayout> mRegionCueBoxes = new ArrayList<CueLayout>();
+ private final TextTrackRegion mRegion;
+
+ private CaptionStyle mCaptionStyle;
+ private float mFontSize;
+
+ public RegionLayout(Context context, TextTrackRegion region, CaptionStyle captionStyle,
+ float fontSize) {
+ super(context);
+
+ mRegion = region;
+ mCaptionStyle = captionStyle;
+ mFontSize = fontSize;
+
+ // TODO: Add support for vertical text
+ setOrientation(VERTICAL);
+
+ if (DEBUG) {
+ setBackgroundColor(DEBUG_REGION_BACKGROUND);
+ }
+ }
+
+ public void setCaptionStyle(CaptionStyle captionStyle, float fontSize) {
+ mCaptionStyle = captionStyle;
+ mFontSize = fontSize;
+
+ final int cueCount = mRegionCueBoxes.size();
+ for (int i = 0; i < cueCount; i++) {
+ final CueLayout cueBox = mRegionCueBoxes.get(i);
+ cueBox.setCaptionStyle(captionStyle, fontSize);
+ }
+ }
+
+ /**
+ * Performs the parent's measurement responsibilities, then
+ * automatically performs its own measurement.
+ */
+ public void measureForParent(int widthMeasureSpec, int heightMeasureSpec) {
+ final TextTrackRegion region = mRegion;
+ final int specWidth = MeasureSpec.getSize(widthMeasureSpec);
+ final int specHeight = MeasureSpec.getSize(heightMeasureSpec);
+ final int width = (int) region.mWidth;
+
+ // Determine the absolute maximum region size as the requested size.
+ final int size = width * specWidth / 100;
+
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(specHeight, MeasureSpec.AT_MOST);
+ measure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ /**
+ * Prepares this region for pruning by setting all tracks as inactive.
+ * <p>
+ * Tracks that are added or updated using {@link #put(TextTrackCue)}
+ * after this calling this method will be marked as active.
+ */
+ public void prepForPrune() {
+ final int cueCount = mRegionCueBoxes.size();
+ for (int i = 0; i < cueCount; i++) {
+ final CueLayout cueBox = mRegionCueBoxes.get(i);
+ cueBox.prepForPrune();
+ }
+ }
+
+ /**
+ * Adds a {@link TextTrackCue} to this region. If the track had already
+ * been added, updates its active state.
+ *
+ * @param cue
+ */
+ public void put(TextTrackCue cue) {
+ final int cueCount = mRegionCueBoxes.size();
+ for (int i = 0; i < cueCount; i++) {
+ final CueLayout cueBox = mRegionCueBoxes.get(i);
+ if (cueBox.getCue() == cue) {
+ cueBox.update();
+ return;
+ }
+ }
+
+ final CueLayout cueBox = new CueLayout(getContext(), cue, mCaptionStyle, mFontSize);
+ addView(cueBox, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+
+ if (getChildCount() > mRegion.mLines) {
+ removeViewAt(0);
+ }
+ }
+
+ /**
+ * Remove all inactive tracks from this region.
+ *
+ * @return true if this region is empty and should be pruned
+ */
+ public boolean prune() {
+ int cueCount = mRegionCueBoxes.size();
+ for (int i = 0; i < cueCount; i++) {
+ final CueLayout cueBox = mRegionCueBoxes.get(i);
+ if (!cueBox.isActive()) {
+ mRegionCueBoxes.remove(i);
+ removeView(cueBox);
+ cueCount--;
+ i--;
+ }
+ }
+
+ return mRegionCueBoxes.isEmpty();
+ }
+
+ /**
+ * @return the region data backing this layout
+ */
+ public TextTrackRegion getRegion() {
+ return mRegion;
+ }
+ }
+
+ /**
+ * A text track cue is the unit of time-sensitive data in a text track,
+ * corresponding for instance for subtitles and captions to the text that
+ * appears at a particular time and disappears at another time.
+ * <p>
+ * A single cue may contain multiple {@link SpanLayout}s, each representing a
+ * single line of text.
+ */
+ private static class CueLayout extends LinearLayout {
+ public final TextTrackCue mCue;
+
+ private CaptionStyle mCaptionStyle;
+ private float mFontSize;
+
+ private boolean mActive;
+ private int mOrder;
+
+ public CueLayout(
+ Context context, TextTrackCue cue, CaptionStyle captionStyle, float fontSize) {
+ super(context);
+
+ mCue = cue;
+ mCaptionStyle = captionStyle;
+ mFontSize = fontSize;
+
+ // TODO: Add support for vertical text.
+ final boolean horizontal = cue.mWritingDirection
+ == TextTrackCue.WRITING_DIRECTION_HORIZONTAL;
+ setOrientation(horizontal ? VERTICAL : HORIZONTAL);
+
+ switch (cue.mAlignment) {
+ case TextTrackCue.ALIGNMENT_END:
+ setGravity(Gravity.END);
+ break;
+ case TextTrackCue.ALIGNMENT_LEFT:
+ setGravity(Gravity.LEFT);
+ break;
+ case TextTrackCue.ALIGNMENT_MIDDLE:
+ setGravity(horizontal
+ ? Gravity.CENTER_HORIZONTAL : Gravity.CENTER_VERTICAL);
+ break;
+ case TextTrackCue.ALIGNMENT_RIGHT:
+ setGravity(Gravity.RIGHT);
+ break;
+ case TextTrackCue.ALIGNMENT_START:
+ setGravity(Gravity.START);
+ break;
+ }
+
+ if (DEBUG) {
+ setBackgroundColor(DEBUG_CUE_BACKGROUND);
+ }
+
+ update();
+ }
+
+ public void setCaptionStyle(CaptionStyle style, float fontSize) {
+ mCaptionStyle = style;
+ mFontSize = fontSize;
+
+ final int n = getChildCount();
+ for (int i = 0; i < n; i++) {
+ final View child = getChildAt(i);
+ if (child instanceof SpanLayout) {
+ ((SpanLayout) child).setCaptionStyle(style, fontSize);
}
}
}
- if (mTextView != null) {
- if (DEBUG) Log.d(TAG, "updating to " + text.toString());
- mTextView.setText(text.toString());
- mTextView.postInvalidate();
+ public void prepForPrune() {
+ mActive = false;
+ }
+
+ public void update() {
+ mActive = true;
+
+ removeAllViews();
+
+ final CaptionStyle captionStyle = mCaptionStyle;
+ final float fontSize = mFontSize;
+ final TextTrackCueSpan[][] lines = mCue.mLines;
+ final int lineCount = lines.length;
+ for (int i = 0; i < lineCount; i++) {
+ final SpanLayout lineBox = new SpanLayout(getContext(), lines[i]);
+ lineBox.setCaptionStyle(captionStyle, fontSize);
+
+ addView(lineBox, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ /**
+ * Performs the parent's measurement responsibilities, then
+ * automatically performs its own measurement.
+ */
+ public void measureForParent(int widthMeasureSpec, int heightMeasureSpec) {
+ final TextTrackCue cue = mCue;
+ final int specWidth = MeasureSpec.getSize(widthMeasureSpec);
+ final int specHeight = MeasureSpec.getSize(heightMeasureSpec);
+ final int direction = getLayoutDirection();
+ final int absAlignment = resolveCueAlignment(direction, cue.mAlignment);
+
+ // Determine the maximum size of cue based on its starting position
+ // and the direction in which it grows.
+ final int maximumSize;
+ switch (absAlignment) {
+ case TextTrackCue.ALIGNMENT_LEFT:
+ maximumSize = 100 - cue.mTextPosition;
+ break;
+ case TextTrackCue.ALIGNMENT_RIGHT:
+ maximumSize = cue.mTextPosition;
+ break;
+ case TextTrackCue.ALIGNMENT_MIDDLE:
+ if (cue.mTextPosition <= 50) {
+ maximumSize = cue.mTextPosition * 2;
+ } else {
+ maximumSize = (100 - cue.mTextPosition) * 2;
+ }
+ break;
+ default:
+ maximumSize = 0;
+ }
+
+ // Determine absolute maximum cue size as the smaller of the
+ // requested size and the maximum theoretical size.
+ final int size = Math.min(cue.mSize, maximumSize) * specWidth / 100;
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(specHeight, MeasureSpec.AT_MOST);
+ measure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ /**
+ * Sets the order of this cue in the list of active cues.
+ *
+ * @param order the order of this cue in the list of active cues
+ */
+ public void setOrder(int order) {
+ mOrder = order;
+ }
+
+ /**
+ * @return whether this cue is marked as active
+ */
+ public boolean isActive() {
+ return mActive;
+ }
+
+ /**
+ * @return the cue data backing this layout
+ */
+ public TextTrackCue getCue() {
+ return mCue;
+ }
+ }
+
+ /**
+ * A text track line represents a single line of text within a cue.
+ * <p>
+ * A single line may contain multiple spans, each representing a section of
+ * text that may be enabled or disabled at a particular time.
+ */
+ private static class SpanLayout extends SubtitleView {
+ private final SpannableStringBuilder mBuilder = new SpannableStringBuilder();
+ private final TextTrackCueSpan[] mSpans;
+
+ public SpanLayout(Context context, TextTrackCueSpan[] spans) {
+ super(context);
+
+ mSpans = spans;
+
+ update();
+ }
+
+ public void update() {
+ final SpannableStringBuilder builder = mBuilder;
+ final TextTrackCueSpan[] spans = mSpans;
+
+ builder.clear();
+ builder.clearSpans();
+
+ final int spanCount = spans.length;
+ for (int i = 0; i < spanCount; i++) {
+ final TextTrackCueSpan span = spans[i];
+ if (span.mEnabled) {
+ builder.append(spans[i].mText);
+ }
+ }
+
+ setText(builder);
+ }
+
+ public void setCaptionStyle(CaptionStyle captionStyle, float fontSize) {
+ setBackgroundColor(captionStyle.backgroundColor);
+ setForegroundColor(captionStyle.foregroundColor);
+ setEdgeColor(captionStyle.edgeColor);
+ setEdgeType(captionStyle.edgeType);
+ setTypeface(captionStyle.getTypeface());
+ setTextSize(fontSize);
}
}
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index b09dbb7..1e3fb40 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1574,12 +1574,12 @@
mSystemThread.installSystemApplicationInfo(info);
synchronized (mSelf) {
- ProcessRecord app = mSelf.newProcessRecordLocked(
- mSystemThread.getApplicationThread(), info,
+ ProcessRecord app = mSelf.newProcessRecordLocked(info,
info.processName, false);
app.persistent = true;
app.pid = MY_PID;
app.maxAdj = ProcessList.SYSTEM_ADJ;
+ app.makeActive(mSystemThread.getApplicationThread(), mSelf.mProcessStats);
mSelf.mProcessNames.put(app.processName, app.uid, app);
synchronized (mSelf.mPidsSelfLocked) {
mSelf.mPidsSelfLocked.put(app.pid, app);
@@ -2282,7 +2282,7 @@
}
if (app == null) {
- app = newProcessRecordLocked(null, info, processName, isolated);
+ app = newProcessRecordLocked(info, processName, isolated);
if (app == null) {
Slog.w(TAG, "Failed making new process record for "
+ processName + "/" + info.uid + " isolated=" + isolated);
@@ -4488,7 +4488,7 @@
EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.userId, app.pid, app.processName);
- app.thread = thread;
+ app.makeActive(thread, mProcessStats);
app.curAdj = app.setAdj = -100;
app.curSchedGroup = app.setSchedGroup = Process.THREAD_GROUP_DEFAULT;
app.forcingToForeground = null;
@@ -7545,8 +7545,8 @@
// GLOBAL MANAGEMENT
// =========================================================
- final ProcessRecord newProcessRecordLocked(IApplicationThread thread,
- ApplicationInfo info, String customProcess, boolean isolated) {
+ final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
+ boolean isolated) {
String proc = customProcess != null ? customProcess : info.processName;
BatteryStatsImpl.Uid.Proc ps = null;
BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
@@ -7574,8 +7574,7 @@
synchronized (stats) {
ps = stats.getProcessStatsLocked(info.uid, proc);
}
- return new ProcessRecord(ps, thread, info, proc, uid,
- mProcessStats.getProcessStateLocked(info.packageName, info.uid, proc));
+ return new ProcessRecord(ps, info, proc, uid);
}
final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated) {
@@ -7587,7 +7586,7 @@
}
if (app == null) {
- app = newProcessRecordLocked(null, info, null, isolated);
+ app = newProcessRecordLocked(info, null, isolated);
mProcessNames.put(info.processName, app.uid, app);
if (isolated) {
mIsolatedProcesses.put(app.uid, app);
@@ -11789,7 +11788,7 @@
app.resetPackageList(mProcessStats);
app.unlinkDeathRecipient();
- app.thread = null;
+ app.makeInactive(mProcessStats);
app.forcingToForeground = null;
app.foregroundServices = false;
app.foregroundActivities = false;
@@ -14693,7 +14692,9 @@
}
private final void setProcessTrackerState(ProcessRecord proc, int memFactor, long now) {
- proc.baseProcessTracker.setState(proc.repProcState, memFactor, now, proc.pkgList);
+ if (proc.thread != null) {
+ proc.baseProcessTracker.setState(proc.repProcState, memFactor, now, proc.pkgList);
+ }
}
private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj,
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index f1a030e..283d122 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -52,13 +52,13 @@
final int uid; // uid of process; may be different from 'info' if isolated
final int userId; // user of process.
final String processName; // name of the process
- final ProcessStats.ProcessState baseProcessTracker;
// List of packages running in the process
final ArrayMap<String, ProcessStats.ProcessState> pkgList
= new ArrayMap<String, ProcessStats.ProcessState>();
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)
+ ProcessStats.ProcessState baseProcessTracker;
int pid; // The process of this application; 0 if none
boolean starting; // True if the process is being started
long lastActivityTime; // For managing the LRU list
@@ -349,18 +349,15 @@
}
}
- ProcessRecord(BatteryStatsImpl.Uid.Proc _batteryStats, IApplicationThread _thread,
- ApplicationInfo _info, String _processName, int _uid,
- ProcessStats.ProcessState tracker) {
+ ProcessRecord(BatteryStatsImpl.Uid.Proc _batteryStats, ApplicationInfo _info,
+ String _processName, int _uid) {
batteryStats = _batteryStats;
info = _info;
isolated = _info.uid != _uid;
uid = _uid;
userId = UserHandle.getUserId(_uid);
processName = _processName;
- baseProcessTracker = tracker;
- pkgList.put(_info.packageName, tracker);
- thread = _thread;
+ pkgList.put(_info.packageName, null);
maxAdj = ProcessList.UNKNOWN_ADJ;
curRawAdj = setRawAdj = -100;
curAdj = setAdj = -100;
@@ -374,7 +371,53 @@
shortStringName = null;
stringName = null;
}
-
+
+ public void makeActive(IApplicationThread _thread, ProcessStatsService tracker) {
+ if (thread == null) {
+ final ProcessStats.ProcessState origBase = baseProcessTracker;
+ if (origBase != null) {
+ origBase.setState(ProcessStats.STATE_NOTHING,
+ tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList);
+ origBase.makeInactive();
+ }
+ baseProcessTracker = tracker.getProcessStateLocked(info.packageName, info.uid,
+ processName);
+ baseProcessTracker.makeActive();
+ for (int i=0; i<pkgList.size(); i++) {
+ ProcessStats.ProcessState ps = pkgList.valueAt(i);
+ if (ps != null && ps != origBase) {
+ ps.makeInactive();
+ }
+ ps = tracker.getProcessStateLocked(pkgList.keyAt(i), info.uid, processName);
+ if (ps != baseProcessTracker) {
+ ps.makeActive();
+ }
+ pkgList.setValueAt(i, ps);
+ }
+ }
+ thread = _thread;
+ }
+
+ public void makeInactive(ProcessStatsService tracker) {
+ if (thread != null) {
+ thread = null;
+ final ProcessStats.ProcessState origBase = baseProcessTracker;
+ if (origBase != null) {
+ origBase.setState(ProcessStats.STATE_NOTHING,
+ tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList);
+ origBase.makeInactive();
+ }
+ baseProcessTracker = null;
+ for (int i=0; i<pkgList.size(); i++) {
+ ProcessStats.ProcessState ps = pkgList.valueAt(i);
+ if (ps != null && ps != origBase) {
+ ps.makeInactive();
+ }
+ pkgList.setValueAt(i, null);
+ }
+ }
+ }
+
/**
* This method returns true if any of the activities within the process record are interesting
* to the user. See HistoryRecord.isInterestingToUserLocked()
@@ -518,10 +561,22 @@
long now = SystemClock.uptimeMillis();
baseProcessTracker.setState(ProcessStats.STATE_NOTHING,
tracker.getMemFactorLocked(), now, pkgList);
- if (pkgList.size() != 1) {
+ final int N = pkgList.size();
+ if (N != 1) {
+ for (int i=0; i<N; i++) {
+ ProcessStats.ProcessState ps = pkgList.valueAt(i);
+ if (ps != null && ps != baseProcessTracker) {
+ ps.makeInactive();
+ }
+
+ }
pkgList.clear();
- pkgList.put(info.packageName, tracker.getProcessStateLocked(
- info.packageName, info.uid, processName));
+ ProcessStats.ProcessState ps = tracker.getProcessStateLocked(
+ info.packageName, info.uid, processName);
+ pkgList.put(info.packageName, ps);
+ if (thread != null && ps != baseProcessTracker) {
+ ps.makeActive();
+ }
}
}
diff --git a/services/java/com/android/server/am/ProcessStatsService.java b/services/java/com/android/server/am/ProcessStatsService.java
index 43ae46f..c180f6e 100644
--- a/services/java/com/android/server/am/ProcessStatsService.java
+++ b/services/java/com/android/server/am/ProcessStatsService.java
@@ -54,12 +54,12 @@
// exists in and the offset into the array to find it. The constants below
// define the encoding of that data in an integer.
- static final int MAX_HISTORIC_STATES = 4; // Maximum number of historic states we will keep.
+ static final int MAX_HISTORIC_STATES = 6; // Maximum number of historic states we will keep.
static final String STATE_FILE_PREFIX = "state-"; // Prefix to use for state filenames.
static final String STATE_FILE_SUFFIX = ".bin"; // Suffix to use for state filenames.
static final String STATE_FILE_CHECKIN_SUFFIX = ".ci"; // State files that have checked in.
static long WRITE_PERIOD = 30*60*1000; // Write file every 30 minutes or so.
- static long COMMIT_PERIOD = 24*60*60*1000; // Commit current stats every day.
+ static long COMMIT_PERIOD = 12*60*60*1000; // Commit current stats every 12 hours.
final ActivityManagerService mAm;
final File mBaseDir;
@@ -132,7 +132,7 @@
ArrayMap<String, ProcessStats.ServiceState> services = pkg.mServices;
for (int k=0; k<services.size(); k++) {
ProcessStats.ServiceState service = services.valueAt(k);
- if (service.isActive()) {
+ if (service.isInUse()) {
if (service.mStartedState != ProcessStats.STATE_NOTHING) {
service.setStarted(true, memFactor, now);
}
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 39756c3..8293bb8 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -318,6 +318,7 @@
if ((serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) {
tracker = ams.mProcessStats.getServiceStateLocked(serviceInfo.packageName,
serviceInfo.applicationInfo.uid, serviceInfo.processName, serviceInfo.name);
+ tracker.makeActive();
}
return tracker;
}