import android.media.AudioManager;
import android.media.session.MediaController;
import android.net.Uri;
+import android.os.BadParcelableException;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@Nullable
public Uri getReferrer() {
Intent intent = getIntent();
- Uri referrer = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
- if (referrer != null) {
- return referrer;
- }
- String referrerName = intent.getStringExtra(Intent.EXTRA_REFERRER_NAME);
- if (referrerName != null) {
- return Uri.parse(referrerName);
+ try {
+ Uri referrer = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
+ if (referrer != null) {
+ return referrer;
+ }
+ String referrerName = intent.getStringExtra(Intent.EXTRA_REFERRER_NAME);
+ if (referrerName != null) {
+ return Uri.parse(referrerName);
+ }
+ } catch (BadParcelableException e) {
+ Log.w(TAG, "Cannot read referrer from intent;"
+ + " intent extras contain unknown custom Parcelable objects");
}
if (mReferrer != null) {
return new Uri.Builder().scheme("android-app").authority(mReferrer).build();
package android.app;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
import android.app.backup.BackupAgent;
}
/**
+ * Creates a new Configuration only if override would modify base. Otherwise returns base.
+ * @param base The base configuration.
+ * @param override The update to apply to the base configuration. Can be null.
+ * @return A Configuration representing base with override applied.
+ */
+ private static Configuration createNewConfigAndUpdateIfNotNull(@NonNull Configuration base,
+ @Nullable Configuration override) {
+ if (override == null) {
+ return base;
+ }
+ Configuration newConfig = new Configuration(base);
+ newConfig.updateFrom(override);
+ return newConfig;
+ }
+
+ /**
* Decides whether to update an Activity's configuration and whether to tell the
* Activity/Component about it.
* @param cb The component callback to notify of configuration change.
* @param activityToken The Activity binder token for which this configuration change happened.
* If the change is global, this is null.
* @param newConfig The new configuration.
- * @param overrideConfig The override config that differentiates the Activity's configuration
+ * @param amOverrideConfig The override config that differentiates the Activity's configuration
* from the base global configuration.
+ * This is supplied by ActivityManager.
* @param reportToActivity Notify the Activity of the change.
*/
private void performConfigurationChanged(ComponentCallbacks2 cb,
IBinder activityToken,
Configuration newConfig,
- Configuration overrideConfig,
+ Configuration amOverrideConfig,
boolean reportToActivity) {
// Only for Activity objects, check that they actually call up to their
// superclass implementation. ComponentCallbacks2 is an interface, so
if ((activity == null) || (activity.mCurrentConfig == null)) {
shouldChangeConfig = true;
} else {
-
// If the new config is the same as the config this Activity
// is already running with then don't bother calling
// onConfigurationChanged
}
}
- if (DEBUG_CONFIGURATION) {
- Slog.v(TAG, "Config callback " + cb + ": shouldChangeConfig=" + shouldChangeConfig);
- }
-
if (shouldChangeConfig) {
+ // Propagate the configuration change to the Activity and ResourcesManager.
+
+ // ContextThemeWrappers may override the configuration for that context.
+ // We must check and apply any overrides defined.
+ Configuration contextThemeWrapperOverrideConfig = null;
+ if (cb instanceof ContextThemeWrapper) {
+ final ContextThemeWrapper contextThemeWrapper = (ContextThemeWrapper) cb;
+ contextThemeWrapperOverrideConfig = contextThemeWrapper.getOverrideConfiguration();
+ }
+
+ // We only update an Activity's configuration if this is not a global
+ // configuration change. This must also be done before the callback,
+ // or else we violate the contract that the new resources are available
+ // in {@link ComponentCallbacks2#onConfigurationChanged(Configuration)}.
if (activityToken != null) {
- // We only update an Activity's configuration if this is not a global
- // configuration change. This must also be done before the callback,
- // or else we violate the contract that the new resources are available
- // in {@link ComponentCallbacks2#onConfigurationChanged(Configuration)}.
- mResourcesManager.updateResourcesForActivity(activityToken, overrideConfig);
+ // Apply the ContextThemeWrapper override if necessary.
+ // NOTE: Make sure the configurations are not modified, as they are treated
+ // as immutable in many places.
+ final Configuration finalOverrideConfig = createNewConfigAndUpdateIfNotNull(
+ amOverrideConfig, contextThemeWrapperOverrideConfig);
+ mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig);
}
if (reportToActivity) {
- Configuration configToReport = newConfig;
-
- if (cb instanceof ContextThemeWrapper) {
- // ContextThemeWrappers may override the configuration for that context.
- // We must check and apply any overrides defined.
- ContextThemeWrapper contextThemeWrapper = (ContextThemeWrapper) cb;
- final Configuration localOverrideConfig =
- contextThemeWrapper.getOverrideConfiguration();
- if (localOverrideConfig != null) {
- configToReport = new Configuration(newConfig);
- configToReport.updateFrom(localOverrideConfig);
- }
- }
-
+ // Apply the ContextThemeWrapper override if necessary.
+ // NOTE: Make sure the configurations are not modified, as they are treated
+ // as immutable in many places.
+ final Configuration configToReport = createNewConfigAndUpdateIfNotNull(
+ newConfig, contextThemeWrapperOverrideConfig);
cb.onConfigurationChanged(configToReport);
}
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
- if (ActivityThread.DEBUG_BROADCAST) {
- int seq = intent.getIntExtra("seq", -1);
- Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction() + " seq=" + seq
- + " to " + mReceiver);
- }
- Args args = new Args(intent, resultCode, data, extras, ordered,
+ final Args args = new Args(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
- if (!mActivityThread.post(args)) {
+ if (intent == null) {
+ Log.wtf(TAG, "Null intent received");
+ } else {
+ if (ActivityThread.DEBUG_BROADCAST) {
+ int seq = intent.getIntExtra("seq", -1);
+ Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction()
+ + " seq=" + seq + " to " + mReceiver);
+ }
+ }
+ if (intent == null || !mActivityThread.post(args)) {
if (mRegistered && ordered) {
IActivityManager mgr = ActivityManagerNative.getDefault();
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
final String whichProp;
final int defaultResId;
if (which == FLAG_LOCK) {
+ /* Factory-default lock wallpapers are not yet supported
whichProp = PROP_LOCK_WALLPAPER;
defaultResId = com.android.internal.R.drawable.default_lock_wallpaper;
+ */
+ return null;
} else {
whichProp = PROP_WALLPAPER;
defaultResId = com.android.internal.R.drawable.default_wallpaper;
}
public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
- //
- // Constants below need to be kept up-to-date with
- // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h
- //
-
- //
- // Error codes for onCameraError
- //
-
- /**
- * Camera has been disconnected
- */
- public static final int ERROR_CAMERA_DISCONNECTED = 0;
- /**
- * Camera has encountered a device-level error
- * Matches CameraDevice.StateCallback#ERROR_CAMERA_DEVICE
- */
- public static final int ERROR_CAMERA_DEVICE = 1;
- /**
- * Camera has encountered a service-level error
- * Matches CameraDevice.StateCallback#ERROR_CAMERA_SERVICE
- */
- public static final int ERROR_CAMERA_SERVICE = 2;
- /**
- * Camera has encountered an error processing a single request.
- */
- public static final int ERROR_CAMERA_REQUEST = 3;
- /**
- * Camera has encountered an error producing metadata for a single capture
- */
- public static final int ERROR_CAMERA_RESULT = 4;
- /**
- * Camera has encountered an error producing an image buffer for a single capture
- */
- public static final int ERROR_CAMERA_BUFFER = 5;
@Override
public IBinder asBinder() {
case ERROR_CAMERA_DEVICE:
case ERROR_CAMERA_SERVICE:
mInError = true;
+ final int publicErrorCode = (errorCode == ERROR_CAMERA_DEVICE) ?
+ StateCallback.ERROR_CAMERA_DEVICE :
+ StateCallback.ERROR_CAMERA_SERVICE;
Runnable r = new Runnable() {
@Override
public void run() {
if (!CameraDeviceImpl.this.isClosed()) {
- mDeviceCallback.onError(CameraDeviceImpl.this, errorCode);
+ mDeviceCallback.onError(CameraDeviceImpl.this, publicErrorCode);
}
}
};
public void run() {
if (!isClosed()) {
mDeviceCallback.onError(CameraDeviceImpl.this,
- CameraDeviceCallbacks.ERROR_CAMERA_SERVICE);
+ StateCallback.ERROR_CAMERA_SERVICE);
}
}
};
* @param queryable a non-{@code null} marshal queryable that supports marshaling {@code T}
*/
public static <T> void registerMarshalQueryable(MarshalQueryable<T> queryable) {
- sRegisteredMarshalQueryables.add(queryable);
+ synchronized(sMarshalLock) {
+ sRegisteredMarshalQueryables.add(queryable);
+ }
}
/**
*/
@SuppressWarnings("unchecked")
public static <T> Marshaler<T> getMarshaler(TypeReference<T> typeToken, int nativeType) {
- // TODO: can avoid making a new token each time by code-genning
- // the list of type tokens and native types from the keys (at the call sites)
- MarshalToken<T> marshalToken = new MarshalToken<T>(typeToken, nativeType);
-
- /*
- * Marshalers are instantiated lazily once they are looked up; successive lookups
- * will not instantiate new marshalers.
- */
- Marshaler<T> marshaler =
- (Marshaler<T>) sMarshalerMap.get(marshalToken);
-
- if (sRegisteredMarshalQueryables.size() == 0) {
- throw new AssertionError("No available query marshalers registered");
- }
+ synchronized(sMarshalLock) {
+ // TODO: can avoid making a new token each time by code-genning
+ // the list of type tokens and native types from the keys (at the call sites)
+ MarshalToken<T> marshalToken = new MarshalToken<T>(typeToken, nativeType);
+
+ /*
+ * Marshalers are instantiated lazily once they are looked up; successive lookups
+ * will not instantiate new marshalers.
+ */
+ Marshaler<T> marshaler =
+ (Marshaler<T>) sMarshalerMap.get(marshalToken);
+
+ if (marshaler == null) {
+
+ if (sRegisteredMarshalQueryables.size() == 0) {
+ throw new AssertionError("No available query marshalers registered");
+ }
- if (marshaler == null) {
- // Query each marshaler to see if they support the native/managed type combination
- for (MarshalQueryable<?> potentialMarshaler : sRegisteredMarshalQueryables) {
+ // Query each marshaler to see if they support the native/managed type combination
+ for (MarshalQueryable<?> potentialMarshaler : sRegisteredMarshalQueryables) {
- MarshalQueryable<T> castedPotential =
- (MarshalQueryable<T>)potentialMarshaler;
+ MarshalQueryable<T> castedPotential =
+ (MarshalQueryable<T>)potentialMarshaler;
- if (castedPotential.isTypeMappingSupported(typeToken, nativeType)) {
- marshaler = castedPotential.createMarshaler(typeToken, nativeType);
- break;
+ if (castedPotential.isTypeMappingSupported(typeToken, nativeType)) {
+ marshaler = castedPotential.createMarshaler(typeToken, nativeType);
+ break;
+ }
}
- }
- if (marshaler == null) {
- throw new UnsupportedOperationException(
+ if (marshaler == null) {
+ throw new UnsupportedOperationException(
"Could not find marshaler that matches the requested " +
- "combination of type reference " +
- typeToken + " and native type " +
- MarshalHelpers.toStringNativeType(nativeType));
+ "combination of type reference " +
+ typeToken + " and native type " +
+ MarshalHelpers.toStringNativeType(nativeType));
+ }
+
+ // Only put when no cached version exists to avoid +0.5ms lookup per call.
+ sMarshalerMap.put(marshalToken, marshaler);
}
- // Only put when no cached version exists to avoid +0.5ms lookup per call.
- sMarshalerMap.put(marshalToken, marshaler);
+ return marshaler;
}
-
- return marshaler;
}
private static class MarshalToken<T> {
}
}
- private static List<MarshalQueryable<?>> sRegisteredMarshalQueryables =
+ // Control access to the static data structures below
+ private static final Object sMarshalLock = new Object();
+
+ private static final List<MarshalQueryable<?>> sRegisteredMarshalQueryables =
new ArrayList<MarshalQueryable<?>>();
- private static HashMap<MarshalToken<?>, Marshaler<?>> sMarshalerMap =
+ private static final HashMap<MarshalToken<?>, Marshaler<?>> sMarshalerMap =
new HashMap<MarshalToken<?>, Marshaler<?>>();
private MarshalRegistry() {
/**
* Current version of checkin data format.
*/
- static final String CHECKIN_VERSION = "17";
+ static final String CHECKIN_VERSION = "18";
/**
* Old version, we hit 9 and ran out of room, need to remove.
};
/**
+ * Return the counter keeping track of the amount of battery discharge while the screen was off,
+ * measured in micro-Ampere-hours. This will be non-zero only if the device's battery has
+ * a coulomb counter.
+ */
+ public abstract LongCounter getDischargeScreenOffCoulombCounter();
+
+ /**
+ * Return the counter keeping track of the amount of battery discharge measured in
+ * micro-Ampere-hours. This will be non-zero only if the device's battery has
+ * a coulomb counter.
+ */
+ public abstract LongCounter getDischargeCoulombCounter();
+
+ /**
* Return the array of discharge step durations.
*/
public abstract LevelStepTracker getDischargeLevelStepTracker();
rawRealtime, which);
final int connChanges = getNumConnectivityChange(which);
final long phoneOnTime = getPhoneOnTime(rawRealtime, which);
+ final long dischargeCount = getDischargeCoulombCounter().getCountLocked(which);
+ final long dischargeScreenOffCount = getDischargeScreenOffCoulombCounter()
+ .getCountLocked(which);
final StringBuilder sb = new StringBuilder(128);
totalRealtime / 1000, totalUptime / 1000,
getStartClockTime(),
whichBatteryScreenOffRealtime / 1000, whichBatteryScreenOffUptime / 1000);
+
// Calculate wakelock times across all uids.
long fullWakeLockTimeTotal = 0;
dumpLine(pw, 0 /* uid */, category, BATTERY_DISCHARGE_DATA,
getDischargeStartLevel()-getDischargeCurrentLevel(),
getDischargeStartLevel()-getDischargeCurrentLevel(),
- getDischargeAmountScreenOn(), getDischargeAmountScreenOff());
+ getDischargeAmountScreenOn(), getDischargeAmountScreenOff(),
+ dischargeCount / 1000, dischargeScreenOffCount / 1000);
} else {
dumpLine(pw, 0 /* uid */, category, BATTERY_DISCHARGE_DATA,
getLowDischargeAmountSinceCharge(), getHighDischargeAmountSinceCharge(),
getDischargeAmountScreenOnSinceCharge(),
- getDischargeAmountScreenOffSinceCharge());
+ getDischargeAmountScreenOffSinceCharge(),
+ dischargeCount / 1000, dischargeScreenOffCount / 1000);
}
if (reqUid < 0) {
formatTimeMs(sb, chargeTimeRemaining / 1000);
pw.println(sb.toString());
}
+
+ final LongCounter dischargeCounter = getDischargeCoulombCounter();
+ final long dischargeCount = dischargeCounter.getCountLocked(which);
+ if (dischargeCount >= 0) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Discharge: ");
+ sb.append(BatteryStatsHelper.makemAh(dischargeCount / 1000.0));
+ sb.append(" mAh");
+ pw.println(sb.toString());
+ }
+
+ final LongCounter dischargeScreenOffCounter = getDischargeScreenOffCoulombCounter();
+ final long dischargeScreenOffCount = dischargeScreenOffCounter.getCountLocked(which);
+ if (dischargeScreenOffCount >= 0) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Screen off discharge: ");
+ sb.append(BatteryStatsHelper.makemAh(dischargeScreenOffCount / 1000.0));
+ sb.append(" mAh");
+ pw.println(sb.toString());
+ }
+
+ final long dischargeScreenOnCount = dischargeCount - dischargeScreenOffCount;
+ if (dischargeScreenOnCount >= 0) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Screen on discharge: ");
+ sb.append(BatteryStatsHelper.makemAh(dischargeScreenOnCount / 1000.0));
+ sb.append(" mAh");
+ pw.println(sb.toString());
+ }
+
pw.print(" Start clock time: ");
pw.println(DateFormat.format("yyyy-MM-dd-HH-mm-ss", getStartClockTime()).toString());
String line = null;
int bytesWrittenInMiB = -1, bytesStashedInMiB = -1;
int timeTotal = -1;
+ int sourceVersion = -1;
while ((line = in.readLine()) != null) {
// Here is an example of lines in last_install:
// ...
if (line.startsWith("time")) {
timeTotal = scaled;
+ } else if (line.startsWith("source_version")) {
+ sourceVersion = scaled;
} else if (line.startsWith("bytes_written")) {
bytesWrittenInMiB = (bytesWrittenInMiB == -1) ? scaled :
bytesWrittenInMiB + scaled;
if (timeTotal != -1) {
MetricsLogger.histogram(context, "ota_time_total", timeTotal);
}
+ if (sourceVersion != -1) {
+ MetricsLogger.histogram(context, "ota_source_version", sourceVersion);
+ }
if (bytesWrittenInMiB != -1) {
MetricsLogger.histogram(context, "ota_written_in_MiBs", bytesWrittenInMiB);
}
private static native void nativeSetLayer(long nativeObject, int zorder);
private static native void nativeSetPosition(long nativeObject, float x, float y);
+ private static native void nativeSetPositionAppliesWithResize(long nativeObject);
private static native void nativeSetSize(long nativeObject, int w, int h);
private static native void nativeSetTransparentRegionHint(long nativeObject, Region region);
private static native void nativeSetAlpha(long nativeObject, float alpha);
nativeSetPosition(mNativeObject, x, y);
}
+ /**
+ * If the size changes in this transaction, position updates specified
+ * in this transaction will not complete until a buffer of the new size
+ * arrives.
+ */
+ public void setPositionAppliesWithResize() {
+ checkNotReleased();
+ nativeSetPositionAppliesWithResize(mNativeObject);
+ }
+
public void setSize(int w, int h) {
checkNotReleased();
nativeSetSize(mNativeObject, w, h);
mSurfaceHolder.setSurfaceFrameSize(mWidth, mHeight);
mSurfaceHolder.mSurfaceLock.unlock();
if (mSurface.isValid()) {
- if (!hadSurface || surfaceGenerationId != mSurface.getGenerationId()) {
+ if (!hadSurface) {
mSurfaceHolder.ungetCallbacks();
mIsCreating = true;
}
surfaceChanged = true;
}
- if (surfaceChanged) {
+ if (surfaceChanged || surfaceGenerationId != mSurface.getGenerationId()) {
mSurfaceHolderCallback.surfaceChanged(mSurfaceHolder,
lp.format, mWidth, mHeight);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
package android.view.accessibility;
+import android.annotation.Nullable;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Gets the title of the window.
*
- * @return The title.
+ * @return The title of the window, or {@code null} if none is available.
*/
+ @Nullable
public CharSequence getTitle() {
return mTitle;
}
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 144 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 146 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
int mDischargeAmountScreenOff;
int mDischargeAmountScreenOffSinceCharge;
+ private LongSamplingCounter mDischargeScreenOffCounter;
+ private LongSamplingCounter mDischargeCounter;
+
static final int MAX_LEVEL_STEPS = 200;
int mInitStepMode = 0;
return mWakeupReasonStats;
}
+ @Override
+ public LongCounter getDischargeScreenOffCoulombCounter() {
+ return mDischargeScreenOffCounter;
+ }
+
+ @Override
+ public LongCounter getDischargeCoulombCounter() {
+ return mDischargeCounter;
+ }
+
public BatteryStatsImpl() {
this(new SystemClocks());
}
final TimeBase mTimeBase;
long mCount;
long mLoadedCount;
- long mLastCount;
long mUnpluggedCount;
long mPluggedCount;
mPluggedCount = in.readLong();
mCount = mPluggedCount;
mLoadedCount = in.readLong();
- mLastCount = 0;
mUnpluggedCount = in.readLong();
timeBase.add(this);
}
}
public long getCountLocked(int which) {
- long val = mCount;
+ long val = mTimeBase.isRunning() ? mCount : mPluggedCount;
if (which == STATS_SINCE_UNPLUGGED) {
val -= mUnpluggedCount;
} else if (which != STATS_SINCE_CHARGED) {
val -= mLoadedCount;
}
-
return val;
}
@Override
public void logState(Printer pw, String prefix) {
pw.println(prefix + "mCount=" + mCount
- + " mLoadedCount=" + mLoadedCount + " mLastCount=" + mLastCount
+ + " mLoadedCount=" + mLoadedCount
+ " mUnpluggedCount=" + mUnpluggedCount
+ " mPluggedCount=" + mPluggedCount);
}
*/
void reset(boolean detachIfReset) {
mCount = 0;
- mLoadedCount = mLastCount = mPluggedCount = mUnpluggedCount = 0;
+ mLoadedCount = mPluggedCount = mUnpluggedCount = 0;
if (detachIfReset) {
detach();
}
void readSummaryFromParcelLocked(Parcel in) {
mLoadedCount = in.readLong();
mCount = mLoadedCount;
- mLastCount = 0;
mUnpluggedCount = mPluggedCount = mLoadedCount;
}
}
mFlashlightOnTimer = new StopwatchTimer(mClocks, null, -9, null, mOnBatteryTimeBase);
mCameraOnTimer = new StopwatchTimer(mClocks, null, -13, null, mOnBatteryTimeBase);
mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase);
+ mDischargeScreenOffCounter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase);
+ mDischargeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
mOnBattery = mOnBatteryInternal = false;
long uptime = mClocks.uptimeMillis() * 1000;
long realtime = mClocks.elapsedRealtime() * 1000;
mDischargeAmountScreenOffSinceCharge = 0;
mDischargeStepTracker.init();
mChargeStepTracker.init();
+ mDischargeScreenOffCounter.reset(false);
+ mDischargeCounter.reset(false);
}
public void resetAllStatsCmdLocked() {
mHistoryCur.states2 |= HistoryItem.STATE2_CHARGING_FLAG;
mHistoryCur.batteryStatus = (byte)status;
mHistoryCur.batteryLevel = (byte)level;
+ mHistoryCur.batteryChargeUAh = chargeUAh;
mMaxChargeStepLevel = mMinDischargeStepLevel =
mLastChargeStepLevel = mLastDischargeStepLevel = level;
mLastChargingStateLevel = level;
mHistoryCur.batteryPlugType = (byte)plugType;
mHistoryCur.batteryTemperature = (short)temp;
mHistoryCur.batteryVoltage = (char)volt;
+ if (chargeUAh < mHistoryCur.batteryChargeUAh) {
+ // Only record discharges
+ final long chargeDiff = mHistoryCur.batteryChargeUAh - chargeUAh;
+ mDischargeCounter.addCountLocked(chargeDiff);
+ mDischargeScreenOffCounter.addCountLocked(chargeDiff);
+ }
mHistoryCur.batteryChargeUAh = chargeUAh;
setOnBatteryLocked(elapsedRealtime, uptime, onBattery, oldStatus, level);
} else {
}
if (chargeUAh >= (mHistoryCur.batteryChargeUAh+10)
|| chargeUAh <= (mHistoryCur.batteryChargeUAh-10)) {
+ if (chargeUAh < mHistoryCur.batteryChargeUAh) {
+ // Only record discharges
+ final long chargeDiff = mHistoryCur.batteryChargeUAh - chargeUAh;
+ mDischargeCounter.addCountLocked(chargeDiff);
+ mDischargeScreenOffCounter.addCountLocked(chargeDiff);
+ }
mHistoryCur.batteryChargeUAh = chargeUAh;
changed = true;
}
mChargeStepTracker.readFromParcel(in);
mDailyDischargeStepTracker.readFromParcel(in);
mDailyChargeStepTracker.readFromParcel(in);
+ mDischargeCounter.readSummaryFromParcelLocked(in);
+ mDischargeScreenOffCounter.readSummaryFromParcelLocked(in);
int NPKG = in.readInt();
if (NPKG > 0) {
mDailyPackageChanges = new ArrayList<>(NPKG);
mChargeStepTracker.writeToParcel(out);
mDailyDischargeStepTracker.writeToParcel(out);
mDailyChargeStepTracker.writeToParcel(out);
+ mDischargeCounter.writeSummaryFromParcelLocked(out);
+ mDischargeScreenOffCounter.writeSummaryFromParcelLocked(out);
if (mDailyPackageChanges != null) {
final int NPKG = mDailyPackageChanges.size();
out.writeInt(NPKG);
mDischargeAmountScreenOffSinceCharge = in.readInt();
mDischargeStepTracker.readFromParcel(in);
mChargeStepTracker.readFromParcel(in);
+ mDischargeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
+ mDischargeScreenOffCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
mLastWriteTime = in.readLong();
mKernelWakelockStats.clear();
out.writeInt(mDischargeAmountScreenOffSinceCharge);
mDischargeStepTracker.writeToParcel(out);
mChargeStepTracker.writeToParcel(out);
+ mDischargeCounter.writeToParcel(out);
+ mDischargeScreenOffCounter.writeToParcel(out);
out.writeLong(mLastWriteTime);
if (inclUids) {
private ActionMode createStandaloneActionMode(ActionMode.Callback callback) {
endOnGoingFadeAnimation();
cleanupPrimaryActionMode();
- if (mPrimaryActionModeView == null) {
+ // We want to create new mPrimaryActionModeView in two cases: if there is no existing
+ // instance at all, or if there is one, but it is detached from window. The latter case
+ // might happen when app is resized in multi-window mode and decor view is preserved
+ // along with the main app window. Keeping mPrimaryActionModeView reference doesn't cause
+ // app memory leaks because killMode() is called when the dismiss animation ends and from
+ // cleanupPrimaryActionMode() invocation above.
+ if (mPrimaryActionModeView == null || !mPrimaryActionModeView.isAttachedToWindow()) {
if (mWindow.isFloating()) {
// Use the action bar theme.
final TypedValue outValue = new TypedValue();
ViewStub stub = (ViewStub) findViewById(R.id.action_mode_bar_stub);
if (stub != null) {
mPrimaryActionModeView = (ActionBarContextView) stub.inflate();
+ mPrimaryActionModePopup = null;
}
}
}
}
if (mPrimaryActionModeView != null) {
endOnGoingFadeAnimation();
+ // Store action mode view reference, so we can access it safely when animation
+ // ends. mPrimaryActionModePopup is set together with mPrimaryActionModeView,
+ // so no need to store reference to it in separate variable.
+ final ActionBarContextView lastActionModeView = mPrimaryActionModeView;
mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
1f, 0f);
mFadeAnim.addListener(new Animator.AnimatorListener() {
+
@Override
public void onAnimationStart(Animator animation) {
@Override
public void onAnimationEnd(Animator animation) {
- mPrimaryActionModeView.setVisibility(GONE);
- if (mPrimaryActionModePopup != null) {
- mPrimaryActionModePopup.dismiss();
+ // If mPrimaryActionModeView has changed - it means that we've
+ // cleared the content while preserving decor view. We don't
+ // want to change the state of new instances accidentally here.
+ if (lastActionModeView == mPrimaryActionModeView) {
+ lastActionModeView.setVisibility(GONE);
+ if (mPrimaryActionModePopup != null) {
+ mPrimaryActionModePopup.dismiss();
+ }
+ lastActionModeView.killMode();
+ mFadeAnim = null;
}
- mPrimaryActionModeView.removeAllViews();
- mFadeAnim = null;
}
@Override
@Override
public void setTitle(CharSequence title) {
+ setTitle(title, true);
+ }
+
+ public void setTitle(CharSequence title, boolean updateAccessibilityTitle) {
if (mTitleView != null) {
mTitleView.setText(title);
} else if (mDecorContentParent != null) {
mDecorContentParent.setWindowTitle(title);
}
mTitle = title;
- WindowManager.LayoutParams params = getAttributes();
- if (!TextUtils.equals(title, params.accessibilityTitle)) {
- params.accessibilityTitle = TextUtils.stringOrSpannedString(title);
- dispatchWindowAttributesChanged(getAttributes());
+ if (updateAccessibilityTitle) {
+ WindowManager.LayoutParams params = getAttributes();
+ if (!TextUtils.equals(title, params.accessibilityTitle)) {
+ params.accessibilityTitle = TextUtils.stringOrSpannedString(title);
+ dispatchWindowAttributesChanged(getAttributes());
+ }
}
}
}
}
+static void nativeSetPositionAppliesWithResize(JNIEnv* env, jclass clazz,
+ jlong nativeObject) {
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+ status_t err = ctrl->setPositionAppliesWithResize();
+ if (err < 0 && err != NO_INIT) {
+ doThrowIAE(env);
+ }
+}
+
static void nativeSetSize(JNIEnv* env, jclass clazz, jlong nativeObject, jint w, jint h) {
SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
status_t err = ctrl->setSize(w, h);
(void*)nativeSetLayer },
{"nativeSetPosition", "(JFF)V",
(void*)nativeSetPosition },
+ {"nativeSetPositionAppliesWithResize", "(J)V",
+ (void*)nativeSetPositionAppliesWithResize },
{"nativeSetSize", "(JII)V",
(void*)nativeSetSize },
{"nativeSetTransparentRegionHint", "(JLandroid/graphics/Region;)V",
<!-- Added in N -->
<protected-broadcast android:name="android.intent.action.ANR" />
<protected-broadcast android:name="android.intent.action.CALL" />
+ <protected-broadcast android:name="android.intent.action.CALL_PRIVILEGED" />
<protected-broadcast android:name="android.intent.action.DROPBOX_ENTRY_ADDED" />
<protected-broadcast android:name="android.intent.action.INPUT_METHOD_CHANGED" />
<protected-broadcast android:name="android.intent.action.internal_sim_state_changed" />
<permission android:name="android.permission.MANAGE_USERS"
android:protectionLevel="signature|privileged" />
+ <!-- @hide Allows an application to create, remove users and get the list of
+ users on the device. Applications holding this permission can only create restricted,
+ guest, managed, and ephemeral users. For creating other kind of users,
+ {@link android.Manifest.permission#MANAGE_USERS} is needed.
+ This permission is not available to third party applications. -->
+ <permission android:name="android.permission.CREATE_USERS"
+ android:protectionLevel="signature" />
+
<!-- @hide Allows an application to set the profile owners and the device owner.
This permission is not available to third party applications.-->
<permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
</plurals>
<string name="now_string_shortest" msgid="8912796667087856402">"indi"</string>
<plurals name="duration_minutes_shortest" formatted="false" msgid="3957499975064245495">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g>dəq</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g>dəq</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g>d</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g>d</item>
</plurals>
<plurals name="duration_hours_shortest" formatted="false" msgid="3552182110578602356">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g>saat</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g>saat</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g>st</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g>st</item>
</plurals>
<plurals name="duration_days_shortest" formatted="false" msgid="5213655532597081640">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g>gün</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g>gün</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g>g</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g>g</item>
</plurals>
<plurals name="duration_years_shortest" formatted="false" msgid="7848711145196397042">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g>il</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g>il</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g>i</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g>i</item>
</plurals>
<plurals name="duration_minutes_shortest_future" formatted="false" msgid="3277614521231489951">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g>dəqiqədə</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g>dəqiqədə</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g>d-də</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g>d-də</item>
</plurals>
<plurals name="duration_hours_shortest_future" formatted="false" msgid="2152452368397489370">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g>saata</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g>saata</item>
+ <item quantity="other"> <xliff:g id="COUNT_1">%d</xliff:g>s-da</item>
+ <item quantity="one"> <xliff:g id="COUNT_0">%d</xliff:g>s-da</item>
</plurals>
<plurals name="duration_days_shortest_future" formatted="false" msgid="8088331502820295701">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g>gündə</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g>gündə</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g>g-də</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g>g-də</item>
</plurals>
<plurals name="duration_years_shortest_future" formatted="false" msgid="2317006667145250301">
<item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g>ildə</item>
</plurals>
<string name="now_string_shortest" msgid="8912796667087856402">"jetzt"</string>
<plurals name="duration_minutes_shortest" formatted="false" msgid="3957499975064245495">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> min</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> min</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> min</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> min</item>
</plurals>
<plurals name="duration_hours_shortest" formatted="false" msgid="3552182110578602356">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> h</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> h</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> h</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> h</item>
</plurals>
<plurals name="duration_days_shortest" formatted="false" msgid="5213655532597081640">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> d</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> d</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> T.</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> T.</item>
</plurals>
<plurals name="duration_years_shortest" formatted="false" msgid="7848711145196397042">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> a</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> a</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> J.</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> J.</item>
</plurals>
<plurals name="duration_minutes_shortest_future" formatted="false" msgid="3277614521231489951">
<item quantity="other">in <xliff:g id="COUNT_1">%d</xliff:g> min</item>
<item quantity="one">in <xliff:g id="COUNT_0">%d</xliff:g> h</item>
</plurals>
<plurals name="duration_days_shortest_future" formatted="false" msgid="8088331502820295701">
- <item quantity="other">in <xliff:g id="COUNT_1">%d</xliff:g> d</item>
- <item quantity="one">in <xliff:g id="COUNT_0">%d</xliff:g> d</item>
+ <item quantity="other">in <xliff:g id="COUNT_1">%d</xliff:g> T.</item>
+ <item quantity="one">in <xliff:g id="COUNT_0">%d</xliff:g> T.</item>
</plurals>
<plurals name="duration_years_shortest_future" formatted="false" msgid="2317006667145250301">
- <item quantity="other">in <xliff:g id="COUNT_1">%d</xliff:g> a</item>
- <item quantity="one">in <xliff:g id="COUNT_0">%d</xliff:g> a</item>
+ <item quantity="other">in <xliff:g id="COUNT_1">%d</xliff:g> J.</item>
+ <item quantity="one">in <xliff:g id="COUNT_0">%d</xliff:g> J.</item>
</plurals>
<plurals name="duration_minutes_relative" formatted="false" msgid="3178131706192980192">
<item quantity="other">vor <xliff:g id="COUNT_1">%d</xliff:g> Minuten</item>
<item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> h</item>
</plurals>
<plurals name="duration_days_shortest" formatted="false" msgid="5213655532597081640">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> días</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> día</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> d</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> d</item>
</plurals>
<plurals name="duration_years_shortest" formatted="false" msgid="7848711145196397042">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> años</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> año</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> a</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> a</item>
</plurals>
<plurals name="duration_minutes_shortest_future" formatted="false" msgid="3277614521231489951">
<item quantity="other">en <xliff:g id="COUNT_1">%d</xliff:g> min</item>
<item quantity="one">en <xliff:g id="COUNT_0">%d</xliff:g> h</item>
</plurals>
<plurals name="duration_days_shortest_future" formatted="false" msgid="8088331502820295701">
- <item quantity="other">en <xliff:g id="COUNT_1">%d</xliff:g> días</item>
- <item quantity="one">en <xliff:g id="COUNT_0">%d</xliff:g> día</item>
+ <item quantity="other">en <xliff:g id="COUNT_1">%d</xliff:g> d</item>
+ <item quantity="one">en <xliff:g id="COUNT_0">%d</xliff:g> d</item>
</plurals>
<plurals name="duration_years_shortest_future" formatted="false" msgid="2317006667145250301">
<item quantity="other">en <xliff:g id="COUNT_1">%d</xliff:g> años</item>
</plurals>
<string name="now_string_shortest" msgid="8912796667087856402">"agora"</string>
<plurals name="duration_minutes_shortest" formatted="false" msgid="3957499975064245495">
- <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> min</item>
- <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> min</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> m</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> m</item>
</plurals>
<plurals name="duration_hours_shortest" formatted="false" msgid="3552182110578602356">
<item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> h</item>
<item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> a</item>
</plurals>
<plurals name="duration_minutes_shortest_future" formatted="false" msgid="3277614521231489951">
- <item quantity="other">en <xliff:g id="COUNT_1">%d</xliff:g> min</item>
- <item quantity="one">en <xliff:g id="COUNT_0">%d</xliff:g> min</item>
+ <item quantity="other">en <xliff:g id="COUNT_1">%d</xliff:g> m</item>
+ <item quantity="one">en <xliff:g id="COUNT_0">%d</xliff:g> m</item>
</plurals>
<plurals name="duration_hours_shortest_future" formatted="false" msgid="2152452368397489370">
<item quantity="other">en <xliff:g id="COUNT_1">%d</xliff:g> h</item>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="byteShort" msgid="8340973892742019101">"Բ"</string>
- <string name="kilobyteShort" msgid="5973789783504771878">"ԿԲ"</string>
+ <string name="kilobyteShort" msgid="5973789783504771878">"կԲ"</string>
<string name="megabyteShort" msgid="6355851576770428922">"ՄԲ"</string>
<string name="gigabyteShort" msgid="3259882455212193214">"ԳԲ"</string>
<string name="terabyteShort" msgid="231613018159186962">"ՏԲ"</string>
<string name="whichEditApplicationNamed" msgid="1775815530156447790">"Խմբագրել հետևյալով՝ %1$s"</string>
<string name="whichEditApplicationLabel" msgid="7183524181625290300">"Փոփոխել"</string>
<string name="whichSendApplication" msgid="6902512414057341668">"Կիսվել"</string>
- <string name="whichSendApplicationNamed" msgid="2799370240005424391">"Տարածել ըստ %1$s"</string>
+ <string name="whichSendApplicationNamed" msgid="2799370240005424391">"Կիսվել %1$s-ի միջոցով"</string>
<string name="whichSendApplicationLabel" msgid="4579076294675975354">"Տրամադրել"</string>
<string name="whichSendToApplication" msgid="8272422260066642057">"Ուղարկել այս հավելվածով"</string>
<string name="whichSendToApplicationNamed" msgid="7768387871529295325">"Ուղարկել %1$s հավելվածով"</string>
<string name="aerr_application_repeated" msgid="3146328699537439573">"<xliff:g id="APPLICATION">%1$s</xliff:g> հավելվածի աշխատանքը շարունակաբար ընդհատվում է"</string>
<string name="aerr_process_repeated" msgid="6235302956890402259">"<xliff:g id="PROCESS">%1$s</xliff:g> գործընթացը շարունակաբար ընդհատվում է"</string>
<string name="aerr_restart" msgid="7581308074153624475">"Կրկին բացել հավելվածը"</string>
- <string name="aerr_report" msgid="5371800241488400617">"Ուղարկել կարծիք"</string>
+ <string name="aerr_report" msgid="5371800241488400617">"Կարծիք հայտնել"</string>
<string name="aerr_close" msgid="2991640326563991340">"Փակել"</string>
<string name="aerr_mute" msgid="1974781923723235953">"Անջատել ձայնը մինչև սարքի վերագործարկումը"</string>
<string name="aerr_wait" msgid="3199956902437040261">"Սպասել"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Lagring"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"åpne bilder, medieinnhold og filer på enheten din"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
- <string name="permgroupdesc_microphone" msgid="4988812113943554584">"spill inn lyd"</string>
+ <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ta opp lyd"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
<string name="permgroupdesc_camera" msgid="3250611594678347720">"ta bilder og ta opp video"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"Telefon"</string>
<string name="user_owner_label" msgid="1119010402169916617">"ਨਿੱਜੀ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
<string name="managed_profile_label" msgid="5289992269827577857">"ਕੰਮ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string>
<string name="permgrouplab_contacts" msgid="3657758145679177612">"ਸੰਪਰਕ"</string>
- <string name="permgroupdesc_contacts" msgid="6951499528303668046">"à¨\86ਪਣà©\87 ਸੰਪਰà¨\95ਾà¨\82 ਨà©\82à©° à¨\90à¨\95ਸà©\88ਸ à¨\95ਰà©\8b"</string>
+ <string name="permgroupdesc_contacts" msgid="6951499528303668046">"à¨\86ਪਣà©\87 ਸੰਪਰà¨\95ਾà¨\82 ਤੱà¨\95 ਪਹà©\81à©°à¨\9a à¨\95ਰਨ"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"ਟਿਕਾਣਾ"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"à¨\87ਸ ਡà©\80ਵਾà¨\88ਸ ਦà©\87 ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਤੱà¨\95 ਪਹà©\81à©°à¨\9aà©\8b"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"à¨\87ਸ ਡà©\80ਵਾà¨\88ਸ ਦà©\87 à¨\9fਿà¨\95ਾਣà©\87 ਤੱà¨\95 ਪਹà©\81à©°à¨\9a à¨\95ਰਨ"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"ਕੈਲੰਡਰ"</string>
- <string name="permgroupdesc_calendar" msgid="3889615280211184106">"à¨\86ਪਣà©\87 à¨\95à©\88ਲੰਡਰ ਦà©\80 à¨\90à¨\95ਸà©\88ਸ à¨\95ਰà©\8b"</string>
+ <string name="permgroupdesc_calendar" msgid="3889615280211184106">"ਤà©\81ਹਾਡà©\87 à¨\95à©\88ਲੰਡਰ ਤੱà¨\95 ਪਹà©\81à©°à¨\9a à¨\95ਰਨ"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
- <string name="permgroupdesc_sms" msgid="4656988620100940350">"SMS ਸà©\81ਨà©\87ਹà©\87 à¨à©\87à¨\9cà©\8b à¨\85ਤà©\87 ਦਿà¨\96ਾà¨\93"</string>
+ <string name="permgroupdesc_sms" msgid="4656988620100940350">"SMS ਸà©\81ਨà©\87ਹà©\87 à¨à©\87à¨\9cਣ à¨\85ਤà©\87 ਦà©\87à¨\96ਣ"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"ਸਟੋਰੇਜ"</string>
- <string name="permgroupdesc_storage" msgid="637758554581589203">"ਆਪਣੀ ਡੀਵਾਈਸ ’ਤੇ ਫੋਟੋਆਂ, ਮੀਡੀਆ ਅਤੇ ਫਾਈਲਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੇਣ"</string>
+ <string name="permgroupdesc_storage" msgid="637758554581589203">"ਆਪਣੀ ਡੀਵਾਈਸ ’ਤੇ ਫੋਟੋਆਂ, ਮੀਡੀਆ ਅਤੇ ਫਾਈਲਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"ਮਾਈਕ੍ਰੋਫੋਨ"</string>
- <string name="permgroupdesc_microphone" msgid="4988812113943554584">"à¨\94ਡà©\80à¨\93 ਰਿà¨\95ਾਰਡ à¨\95ਰà©\8b"</string>
+ <string name="permgroupdesc_microphone" msgid="4988812113943554584">"à¨\94ਡà©\80à¨\93 ਰਿà¨\95ਾਰਡ à¨\95ਰਨ"</string>
<string name="permgrouplab_camera" msgid="4820372495894586615">"ਕੈਮਰਾ"</string>
- <string name="permgroupdesc_camera" msgid="3250611594678347720">"ਤਸਵà©\80ਰਾà¨\82 à¨\96ਿੱà¨\9aà©\8b à¨\85ਤà©\87 ਵà©\80ਡà©\80à¨\93 ਰਿà¨\95ਾਰਡ à¨\95ਰà©\8b"</string>
+ <string name="permgroupdesc_camera" msgid="3250611594678347720">"ਤਸਵà©\80ਰਾà¨\82 ਲà©\88ਣ à¨\85ਤà©\87 ਵà©\80ਡà©\80à¨\93 ਰਿà¨\95ਾਰਡ à¨\95ਰਨ"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"ਫੋਨ"</string>
- <string name="permgroupdesc_phone" msgid="6234224354060641055">"ਫ਼à©\8bਨ à¨\95ਾਲਾà¨\82 à¨\95ਰà©\8b à¨\85ਤà©\87 à¨\89ਹਨਾà¨\82 ਨà©\82à©° ਪà©\8dਰਬੰਧਿਤ à¨\95ਰà©\8b"</string>
+ <string name="permgroupdesc_phone" msgid="6234224354060641055">"ਫ਼à©\8bਨ à¨\95ਾਲਾà¨\82 à¨\95ਰਨ à¨\85ਤà©\87 à¨\89ਹਨਾà¨\82 ਦਾ ਪà©\8dਰਬੰਧਨ à¨\95ਰਨ"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"ਸਰੀਰ ਸੰਵੇਦਕ"</string>
- <string name="permgroupdesc_sensors" msgid="7147968539346634043">"à¨\86ਪਣà©\87 ਮਹੱਤਵਪà©\82ਰਣ ਲੱà¨\9bਣਾà¨\82 ਬਾਰà©\87 ਸੰਵà©\87ਦà¨\95 ਡà©\88à¨\9fਾ ਤੱà¨\95 ਪਹà©\81à©°à¨\9a"</string>
+ <string name="permgroupdesc_sensors" msgid="7147968539346634043">"à¨\86ਪਣà©\87 ਸਰà©\80ਰ ਦà©\87 à¨\85ਹਿਮ à¨\9aਿੰਨà©\8dਹਾà¨\82 ਬਾਰà©\87 ਸੰਵà©\87ਦà¨\95 ਡà©\88à¨\9fà©\87 ਤੱà¨\95 ਪਹà©\81à©°à¨\9a à¨\95ਰਨ"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"ਵਿੰਡੋ ਸਮੱਗਰੀ ਮੁੜ ਪ੍ਰਾਪਤ ਕਰੋ"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"ਇੱਕ ਵਿੰਡੋ ਦੀ ਸਮੱਗਰੀ ਦੀ ਜਾਂਚ ਕਰੋ, ਜਿਸ ਨਾਲ ਤੁਸੀਂ ਇੰਟਰੈਕਟ ਕਰ ਰਹੇ ਹੋ।"</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"ਐਕਸਪਲੋਰ ਬਾਇ ਟਚ ਚਾਲੂ ਕਰੋ"</string>
<string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"ਐਪ ਨੂੰ ਤੁਹਾਡੀ ਡੀਵਾਈਸ ਵੱਲੋਂ ਪ੍ਰਾਪਤ ਕੀਤੇ ਸੈਲ ਪ੍ਰਸਾਰਨ ਸੁਨੇਹੇ ਪੜ੍ਹਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਸੈਲ ਪ੍ਰਸਾਰਨ ਚਿਤਾਵਨੀਆਂ ਤੁਹਾਨੂੰ ਐਮਰਜੈਂਸੀ ਸਥਿਤੀਆਂ ਦੀ ਚਿਤਾਵਨੀ ਦੇਣ ਲਈ ਕੁਝ ਨਿਰਧਾਰਿਤ ਸਥਾਨਾਂ ਤੇ ਪ੍ਰਦਾਨ ਕੀਤੀਆਂ ਜਾਂਦੀਆਂ ਹਨ। ਖ਼ਰਾਬ ਐਪਸ ਤੁਹਾਡੀ ਡੀਵਾਈਸ ਦੇ ਪ੍ਰਦਰਸ਼ਨ ਜਾਂ ਓਪਰੇਸ਼ਨ ਵਿੱਚ ਵਿਘਨ ਪਾ ਸਕਦੇ ਹਨ ਜਦੋਂ ਇੱਕ ਐਮਰਜੈਂਸੀ ਸੈਲ ਪ੍ਰਸਾਰਨ ਪ੍ਰਾਪਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ।"</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"ਸਬਸਕ੍ਰਾਈਬ ਕੀਤੇ ਫੀਡਸ ਪੜ੍ਹੋ"</string>
<string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"ਐਪ ਨੂੰ ਵਰਤਮਾਨ ਵਿੱਚ ਸਿੰਕ ਕੀਤੇ ਫੀਡਸ ਬਾਰੇ ਵੇਰਵੇ ਪ੍ਰਾਪਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
- <string name="permlab_sendSms" msgid="7544599214260982981">"SMS ਸà©\81ਨà©\87ਹà©\87 à¨à©\87à¨\9cà©\8b à¨\85ਤà©\87 ਦਿà¨\96ਾà¨\93"</string>
+ <string name="permlab_sendSms" msgid="7544599214260982981">"SMS ਸà©\81ਨà©\87ਹà©\87 à¨à©\87à¨\9cਣ à¨\85ਤà©\87 ਦà©\87à¨\96ਣ"</string>
<string name="permdesc_sendSms" msgid="7094729298204937667">"ਐਪ ਨੂੰ SMS ਸੁਨੇਹੇ ਭੇਜਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਸਦੇ ਸਿੱਟੇ ਵਜੋਂ ਅਕਲਪਿਤ ਖ਼ਰਚੇ ਪੈ ਸਕਦੇ ਹਨ। ਖ਼ਰਾਬ ਐਪਸ ਤੁਹਾਡੀ ਪੁਸ਼ਟੀ ਤੋਂ ਬਿਨਾਂ ਸੁਨੇਹੇ ਭੇਜ ਕੇ ਤੁਹਾਨੂੰ ਖ਼ਰਚੇ ਪਾ ਸਕਦੇ ਹਨ।"</string>
<string name="permlab_readSms" msgid="8745086572213270480">"ਤੁਹਾਡੇ ਟੈਕਸਟ ਸੁਨੇਹੇ (SMS ਜਾਂ MMS) ਪੜ੍ਹੋ"</string>
<string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"ਐਪ ਨੂੰ ਤੁਹਾਡੀ ਟੈਬਲੇਟ ਜਾਂ SIM ਕਾਰਡ ਤੇ ਸਟੋਰ ਕੀਤੇ SMS ਸੁਨੇਹੇ ਪੜ੍ਹਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਐਪ ਨੂੰ ਸਾਰੇ SMS ਸੁਨੇਹੇ ਪੜ੍ਹਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ, ਸਮੱਗਰੀ ਜਾਂ ਗੁਪਤਤਾ ਤੇ ਧਿਆਨ ਦਿੱਤੇ ਬਿਨਾਂ।"</string>
<!-- Window attributes -->
<item name="windowBackground">@drawable/screen_background_selector_dark</item>
+ <item name="windowBackgroundFallback">?attr/colorBackground</item>
<item name="windowClipToOutline">false</item>
<item name="windowFrame">@null</item>
<item name="windowNoTitle">false</item>
<li>48x48 (1.0x baseline) for medium-density</li>
<li>72x72 (1.5x) for high-density</li>
<li>96x96 (2.0x) for extra-high-density</li>
- <li>180x180 (3.0x) for extra-extra-high-density</li>
+ <li>144x144 (3.0x) for extra-extra-high-density</li>
<li>192x192 (4.0x) for extra-extra-extra-high-density (launcher icon only; see
<a href="#xxxhdpi-note">note</a> above)</li>
</ul>
<a href="{@docRoot}reference/android/support/customtabs/CustomTabsService.html">Custom Tabs
Service</a>
and
-<a href="{@docRoot}reference/android/support/customtabs/CustomTabsSCallback.html">Custom Tabs
+<a href="{@docRoot}reference/android/support/customtabs/CustomTabsCallback.html">Custom Tabs
Callback</a>. </p>
mDevice = UiDevice.getInstance(getInstrumentation());
// Perform a short press on the HOME button
-mDevice().pressHome();
+mDevice.pressHome();
// Bring up the default launcher by searching for
// a UI component that matches the content-description for the launcher button
as your activity's app bar:
<ol>
- <li>Add the the
+ <li>Add the
<a href="{@docRoot}tools/support-library/features.html#v7-appcompat">v7
appcompat</a> support library to your project, as described in <a href=
"{@docRoot}tools/support-library/setup.html">Support Library Setup</a>.
</p>
<ul>
- <li>Rendering Performance 101
+ <li>
+ <a class="external-link" href="https://www.youtube.com/watch?v=HXQhu6qfTVU">
+ Rendering Performance 101</a>
</li>
- <li>Why 60fps?
+ <li>
+ <a class="external-link" href="https://www.youtube.com/watch?v=CaMTIgxCSqU">
+ Why 60fps?</a>
</li>
- <li>Android UI and the GPU
+ <li>
+ <a class="external-link" href="https://www.youtube.com/watch?v=WH9AFhgwmDw">
+ Android, UI, and the GPU</a>
</li>
- <li>Invalidations Layouts and performance
+ <li>
+ <a class="external-link" href="https://www.youtube.com/watch?v=we6poP0kw6E">
+ Invalidations, Layouts, and Performance</a>
</li>
- <li>Analyzing UI Performance with Systrace
+ <li>
+ <a href="{@docRoot}studio/profile/systrace.html">
+ Analyzing UI Performance with Systrace</a>
</li>
</ul>
tests/unit/MatrixTests.cpp \
tests/unit/OffscreenBufferPoolTests.cpp \
tests/unit/RenderNodeTests.cpp \
+ tests/unit/RenderPropertiesTests.cpp \
tests/unit/SkiaBehaviorTests.cpp \
tests/unit/SnapshotTests.cpp \
tests/unit/StringUtilsTests.cpp \
offscreenBuffer->texture.id(), 0);
GL_CHECKPOINT(LOW);
- LOG_ALWAYS_FATAL_IF(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE,
- "framebuffer incomplete!");
+ int status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ LOG_ALWAYS_FATAL_IF(status != GL_FRAMEBUFFER_COMPLETE,
+ "framebuffer incomplete, status %d, textureId %d, size %dx%d",
+ status,
+ offscreenBuffer->texture.id(),
+ offscreenBuffer->texture.width(),
+ offscreenBuffer->texture.height());
// Change the viewport & ortho projection
setViewport(offscreenBuffer->viewportWidth, offscreenBuffer->viewportHeight);
ATRACE_FORMAT("Optimize HW Layer DisplayList %s %ux%u",
layerNode->getName(), layerNode->getWidth(), layerNode->getHeight());
- const Rect& layerDamage = layers.entries()[i].damage;
+ Rect layerDamage = layers.entries()[i].damage;
+ // TODO: ensure layer damage can't be larger than layer
+ layerDamage.doIntersect(0, 0, layer->viewportWidth, layer->viewportHeight);
layerNode->computeOrdering();
// map current light center into RenderNode's coordinate space
transformUpdateNeeded = true;
} else if (!layerMatchesWidthAndHeight(mLayer, getWidth(), getHeight())) {
#if HWUI_NEW_OPS
+ // TODO: remove now irrelevant, currently enqueued damage (respecting damage ordering)
+ // Or, ideally, maintain damage between frames on node/layer so ordering is always correct
RenderState& renderState = mLayer->renderState;
if (properties().fitsOnLayer()) {
mLayer = renderState.layerPool().resize(mLayer, getWidth(), getHeight());
bool fitsOnLayer() const {
const DeviceInfo* deviceInfo = DeviceInfo::get();
return mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize()
- && mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize();
+ && mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize()
+ && mPrimitiveFields.mWidth > 0
+ && mPrimitiveFields.mHeight > 0;
}
bool promotedToLayer() const {
--- /dev/null
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <RenderProperties.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+TEST(RenderProperties, layerValidity) {
+ DeviceInfo::initialize();
+
+ const int maxTextureSize = DeviceInfo::get()->maxTextureSize();
+ ASSERT_LE(2048, maxTextureSize);
+ ASSERT_GT(100000, maxTextureSize);
+
+ RenderProperties props;
+
+ // simple cases that all should fit on layers
+ props.setLeftTopRightBottom(0, 0, 100, 100);
+ ASSERT_TRUE(props.fitsOnLayer());
+ props.setLeftTopRightBottom(100, 2000, 300, 4000);
+ ASSERT_TRUE(props.fitsOnLayer());
+ props.setLeftTopRightBottom(-10, -10, 510, 512);
+ ASSERT_TRUE(props.fitsOnLayer());
+
+ // Too big - can't have layer bigger than max texture size
+ props.setLeftTopRightBottom(0, 0, maxTextureSize + 1, maxTextureSize + 1);
+ ASSERT_FALSE(props.fitsOnLayer());
+
+ // Too small - can't have 0 dimen layer
+ props.setLeftTopRightBottom(0, 0, 100, 0);
+ ASSERT_FALSE(props.fitsOnLayer());
+}
import static android.os.Environment.STANDARD_DIRECTORIES;
import static android.os.storage.StorageVolume.EXTRA_DIRECTORY_NAME;
import static android.os.storage.StorageVolume.EXTRA_STORAGE_VOLUME;
+
import static com.android.documentsui.LocalPreferences.getScopedAccessPermissionStatus;
import static com.android.documentsui.LocalPreferences.PERMISSION_ASK;
import static com.android.documentsui.LocalPreferences.PERMISSION_ASK_AGAIN;
final List<VolumeInfo> volumes = sm.getVolumes();
if (DEBUG) Log.d(TAG, "Number of volumes: " + volumes.size());
File internalRoot = null;
+ boolean found = true;
for (VolumeInfo volume : volumes) {
if (isRightVolume(volume, root, userId)) {
+ found = true;
internalRoot = volume.getInternalPathForUser(userId);
// Must convert path before calling getDocIdForFileCreateNewDir()
if (DEBUG) Log.d(TAG, "Converting " + root + " to " + internalRoot);
file = isRoot ? internalRoot : new File(internalRoot, directory);
+ volumeUuid = storageVolume.getUuid();
volumeLabel = sm.getBestVolumeDescription(volume);
- volumeUuid = volume.getFsUuid();
+ if (TextUtils.isEmpty(volumeLabel)) {
+ volumeLabel = storageVolume.getDescription(activity);
+ }
+ if (TextUtils.isEmpty(volumeLabel)) {
+ volumeLabel = activity.getString(android.R.string.unknownName);
+ Log.w(TAG, "No volume description for " + volume + "; using " + volumeLabel);
+ }
break;
}
}
return true;
}
- if (volumeLabel == null) {
+ if (!found) {
Log.e(TAG, "Could not get volume for " + file);
logInvalidScopedAccessRequest(activity, SCOPED_DIRECTORY_ACCESS_ERROR);
return false;
private static boolean isRightVolume(VolumeInfo volume, String root, int userId) {
final File userPath = volume.getPathForUser(userId);
final String path = userPath == null ? null : volume.getPathForUser(userId).getPath();
- final boolean isVisible = volume.isVisibleForWrite(userId);
+ final boolean isMounted = volume.isMountedReadable();
if (DEBUG)
- Log.d(TAG, "Volume: " + volume + " userId: " + userId + " root: " + root
- + " volumePath: " + volume.getPath().getPath()
- + " pathForUser: " + path
- + " internalPathForUser: " + volume.getInternalPath()
- + " isVisible: " + isVisible);
-
- return volume.isVisibleForWrite(userId) && root.equals(path);
+ Log.d(TAG, "Volume: " + volume
+ + "\n\tuserId: " + userId
+ + "\n\tuserPath: " + userPath
+ + "\n\troot: " + root
+ + "\n\tpath: " + path
+ + "\n\tisMounted: " + isMounted);
+
+ return isMounted && root.equals(path);
}
private static Uri getGrantedUriPermission(Context context, ContentProviderClient provider,
message = TextUtils.expandTemplate(
getText(mIsPrimary ? R.string.open_external_dialog_request_primary_volume
: R.string.open_external_dialog_request),
- mAppLabel, directory, mVolumeLabel);
+ mAppLabel, directory, mVolumeLabel);
}
final TextView messageField = (TextView) view.findViewById(R.id.message);
messageField.setText(message);
<string name="plugin_vendor_samsung">Samsung</string>
<string name="plugin_vendor_epson">Epson</string>
<string name="plugin_vendor_konica_minolta">Konica Minolta</string>
- <string name="plugin_vendor_fuji">Fuji</string>
+ <string name="plugin_vendor_fuji_xerox">Fuji Xerox</string>
<string name="plugin_vendor_morpia">Mopria</string>
</resources>
</vendor>
<vendor>
- <name>@string/plugin_vendor_fuji</name>
+ <name>@string/plugin_vendor_fuji_xerox</name>
<package>jp.co.fujixerox.prt.PrintUtil.PCL</package>
<mdns-names>
<mdns-name>FUJI XEROX</mdns-name>
String usbMfg = getString(attributes.get(ATTRIBUTE__USB_MFG));
String usbMdl = getString(attributes.get(ATTRIBUTE__USB_MDL));
String mfg = getString(attributes.get(ATTRIBUTE__MFG));
- return containsVendor(product, vendorValues) || containsVendor(ty, vendorValues) || containsVendor(usbMfg, vendorValues) || containsVendor(mfg, vendorValues) && !(containsString(ty, EXCLUDE_FUJI) || containsString(product, EXCLUDE_FUJI) || containsString(usbMdl, EXCLUDE_FUJI));
-
+ return (containsVendor(product, vendorValues) || containsVendor(ty, vendorValues) ||
+ containsVendor(usbMfg, vendorValues) || containsVendor(mfg, vendorValues)) &&
+ !(containsString(ty, EXCLUDE_FUJI) || containsString(product, EXCLUDE_FUJI) ||
+ containsString(usbMdl, EXCLUDE_FUJI));
}
public static String getVendor(NsdServiceInfo networkDevice) {
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
+
+ mMediaSizeComparator.onConfigurationChanged(newConfig);
+
if (mPrintPreviewController != null) {
mPrintPreviewController.onOrientationChanged();
}
package com.android.printspooler.util;
+import android.annotation.NonNull;
import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.print.PrintAttributes.MediaSize;
-import android.util.ArrayMap;
import com.android.printspooler.R;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.Map;
/**
*/
public final class MediaSizeUtils {
- private static Map<MediaSize, String> sMediaSizeToStandardMap;
+ private static Map<MediaSize, Integer> sMediaSizeToStandardMap;
+
+ /** The media size standard for all media sizes no standard is defined for */
+ private static int sMediaSizeStandardIso;
private MediaSizeUtils() {
/* do nothing - hide constructor */
return MediaSize.getStandardMediaSizeById(mediaSizeId);
}
- private static String getStandardForMediaSize(Context context, MediaSize mediaSize) {
+ /**
+ * Get the standard the {@link MediaSize} belongs to.
+ *
+ * @param context The context of the caller
+ * @param mediaSize The {@link MediaSize} to be resolved
+ *
+ * @return The standard the {@link MediaSize} belongs to
+ */
+ private static int getStandardForMediaSize(Context context, MediaSize mediaSize) {
if (sMediaSizeToStandardMap == null) {
- sMediaSizeToStandardMap = new ArrayMap<MediaSize, String>();
+ sMediaSizeStandardIso = Integer.parseInt(context.getString(
+ R.string.mediasize_standard_iso));
+
+ sMediaSizeToStandardMap = new HashMap<>();
String[] mediaSizeToStandardMapValues = context.getResources()
.getStringArray(R.array.mediasize_to_standard_map);
final int mediaSizeToStandardCount = mediaSizeToStandardMapValues.length;
for (int i = 0; i < mediaSizeToStandardCount; i += 2) {
String mediaSizeId = mediaSizeToStandardMapValues[i];
MediaSize key = MediaSize.getStandardMediaSizeById(mediaSizeId);
- String value = mediaSizeToStandardMapValues[i + 1];
+ int value = Integer.parseInt(mediaSizeToStandardMapValues[i + 1]);
sMediaSizeToStandardMap.put(key, value);
}
}
- String standard = sMediaSizeToStandardMap.get(mediaSize);
- return (standard != null) ? standard : context.getString(
- R.string.mediasize_standard_iso);
+ Integer standard = sMediaSizeToStandardMap.get(mediaSize);
+ return (standard != null) ? standard : sMediaSizeStandardIso;
}
/**
public static final class MediaSizeComparator implements Comparator<MediaSize> {
private final Context mContext;
+ /** Current configuration */
+ private Configuration mCurrentConfig;
+
+ /** The standard to use for the current locale */
+ private int mCurrentStandard;
+
+ /** Mapping from media size to label */
+ private final @NonNull Map<MediaSize, String> mMediaSizeToLabel;
+
public MediaSizeComparator(Context context) {
mContext = context;
+ mMediaSizeToLabel = new HashMap<>();
+ mCurrentStandard = Integer.parseInt(mContext.getString(R.string.mediasize_standard));
+ }
+
+ /**
+ * Handle a configuration change by reloading all resources.
+ *
+ * @param newConfig The new configuration that will be applied.
+ */
+ public void onConfigurationChanged(@NonNull Configuration newConfig) {
+ if (mCurrentConfig == null ||
+ (newConfig.diff(mCurrentConfig) & ActivityInfo.CONFIG_LOCALE) != 0) {
+ mCurrentStandard = Integer
+ .parseInt(mContext.getString(R.string.mediasize_standard));
+ mMediaSizeToLabel.clear();
+
+ mCurrentConfig = newConfig;
+ }
+ }
+
+ /**
+ * Get the label for a {@link MediaSize}.
+ *
+ * @param context The context the label should be loaded for
+ * @param mediaSize The {@link MediaSize} to resolve
+ *
+ * @return The label for the media size
+ */
+ public @NonNull String getLabel(@NonNull Context context, @NonNull MediaSize mediaSize) {
+ String label = mMediaSizeToLabel.get(mediaSize);
+
+ if (label == null) {
+ label = mediaSize.getLabel(context.getPackageManager());
+ mMediaSizeToLabel.put(mediaSize, label);
+ }
+
+ return label;
}
@Override
public int compare(MediaSize lhs, MediaSize rhs) {
- String currentStandard = mContext.getString(R.string.mediasize_standard);
- String lhsStandard = getStandardForMediaSize(mContext, lhs);
- String rhsStandard = getStandardForMediaSize(mContext, rhs);
+ int lhsStandard = getStandardForMediaSize(mContext, lhs);
+ int rhsStandard = getStandardForMediaSize(mContext, rhs);
// The current standard always wins.
- if (lhsStandard.equals(currentStandard)) {
- if (!rhsStandard.equals(currentStandard)) {
+ if (lhsStandard == mCurrentStandard) {
+ if (rhsStandard != mCurrentStandard) {
return -1;
}
- } else if (rhsStandard.equals(currentStandard)) {
+ } else if (rhsStandard == mCurrentStandard) {
return 1;
}
- if (!lhsStandard.equals(rhsStandard)) {
+ if (lhsStandard != rhsStandard) {
// Different standards - use the standard ordering.
- return lhsStandard.compareTo(rhsStandard);
+ return Integer.valueOf(lhsStandard).compareTo(rhsStandard);
} else {
// Same standard - sort alphabetically by label.
- return lhs.getLabel(mContext.getPackageManager()).
- compareTo(rhs.getLabel(mContext.getPackageManager()));
+ return getLabel(mContext, lhs).compareTo(getLabel(mContext, rhs));
}
}
}
<string name="transition_animation_scale_title" msgid="387527540523595875">"Scale ng transition animation"</string>
<string name="animator_duration_scale_title" msgid="3406722410819934083">"Scale ng tagal ng animator"</string>
<string name="overlay_display_devices_title" msgid="5364176287998398539">"I-simulate, ika-2 display"</string>
- <string name="debug_applications_category" msgid="4206913653849771549">"Apps"</string>
+ <string name="debug_applications_category" msgid="4206913653849771549">"Mga App"</string>
<string name="immediately_destroy_activities" msgid="1579659389568133959">"Huwag magtago ng mga aktibidad"</string>
<string name="immediately_destroy_activities_summary" msgid="3592221124808773368">"Sirain ang bawat aktibidad sa sandaling iwan ito ng user"</string>
<string name="app_process_limit_title" msgid="4280600650253107163">"Limitasyon ng proseso sa background"</string>
<uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
- <uses-permission android:name="android.permission.MANAGE_USERS" />
+ <uses-permission android:name="android.permission.CREATE_USERS" />
<uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
<uses-permission android:name="android.permission.BLUETOOTH_STACK" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
if (mListening) {
if (mPosition != position) {
// Clear out the last pages from listening.
- mPages.get(mPosition).setListening(false);
+ setPageListening(mPosition, false);
if (mOffPage) {
- mPages.get(mPosition + 1).setListening(false);
+ setPageListening(mPosition + 1, false);
}
// Set the new pages to listening
- mPages.get(position).setListening(true);
+ setPageListening(position, true);
if (offPage) {
- mPages.get(position + 1).setListening(true);
+ setPageListening(position + 1, true);
}
} else if (mOffPage != offPage) {
// Whether we are showing position + 1 has changed.
- mPages.get(mPosition + 1).setListening(offPage);
+ setPageListening(mPosition + 1, offPage);
}
}
// Save the current state.
mOffPage = offPage;
}
+ private void setPageListening(int position, boolean listening) {
+ if (position >= mPages.size()) return;
+ mPages.get(position).setListening(listening);
+ }
+
@Override
public boolean hasOverlappingRendering() {
return false;
private void updateQsState() {
boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling || mHeaderAnimating;
mQSPanel.setExpanded(mQsExpanded);
+ mQSDetail.setExpanded(mQsExpanded);
mHeader.setVisibility((mQsExpanded || !mKeyguardShowing || mHeaderAnimating)
? View.VISIBLE
: View.INVISIBLE);
import android.widget.LinearLayout;
import android.widget.Switch;
import android.widget.TextView;
+
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
private boolean mFullyExpanded;
private View mQsDetailHeaderBack;
private BaseStatusBarHeader mHeader;
+ private boolean mTriggeredExpand;
+ private int mOpenX;
+ private int mOpenY;
public QSDetail(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
public void setQsPanel(QSPanel panel, BaseStatusBarHeader header) {
mQsPanel = panel;
mHeader = header;
+ mHeader.setCallback(mQsPanelCallback);
mQsPanel.setCallback(mQsPanelCallback);
}
mFullyExpanded = fullyExpanded;
}
+ public void setExpanded(boolean qsExpanded) {
+ if (!qsExpanded) {
+ mTriggeredExpand = false;
+ }
+ }
+
private void updateDetailText() {
mDetailDoneButton.setText(R.string.quick_settings_done);
mDetailSettingsButton.setText(R.string.quick_settings_more_settings);
}
});
}
+ if (!mFullyExpanded) {
+ mTriggeredExpand = true;
+ mHost.animateToggleQSExpansion();
+ } else {
+ mTriggeredExpand = false;
+ }
+ mOpenX = x;
+ mOpenY = y;
+ } else {
+ // Ensure we collapse into the same point we opened from.
+ x = mOpenX;
+ y = mOpenY;
+ if (mTriggeredExpand) {
+ mHost.animateToggleQSExpansion();
+ mTriggeredExpand = false;
+ }
}
boolean visibleDiff = (mDetailAdapter != null) != (adapter != null);
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
private int mPanelPaddingBottom;
private int mBrightnessPaddingTop;
- private boolean mExpanded;
+ protected boolean mExpanded;
protected boolean mListening;
private Callback mCallback;
private QSCustomizer mCustomizePanel;
private Record mDetailRecord;
- private boolean mTriggeredExpand;
public QSPanel(Context context) {
this(context, null);
}
MetricsLogger.visibility(mContext, MetricsEvent.QS_PANEL, mExpanded);
if (!mExpanded) {
- mTriggeredExpand = false;
closeDetail();
} else {
logTiles();
public void setTiles(Collection<QSTile<?>> tiles, boolean collapsedView) {
for (TileRecord record : mRecords) {
mTileLayout.removeTile(record);
+ record.tile.removeCallback(record.callback);
}
mRecords.clear();
for (QSTile<?> tile : tiles) {
return new QSTileView(mContext, tile.createTileView(mContext), collapsedView);
}
+ protected boolean shouldShowDetail() {
+ return mExpanded;
+ }
+
protected void addTile(final QSTile<?> tile, boolean collapsedView) {
final TileRecord r = new TileRecord();
r.tile = tile;
@Override
public void onShowDetail(boolean show) {
- QSPanel.this.showDetail(show, r);
+ // Both the collapsed and full QS panels get this callback, this check determines
+ // which one should handle showing the detail.
+ if (shouldShowDetail()) {
+ QSPanel.this.showDetail(show, r);
+ }
}
@Override
}
};
r.tile.addCallback(callback);
+ r.callback = callback;
final View.OnClickListener click = new View.OnClickListener() {
@Override
public void onClick(View v) {
}
protected void handleShowDetail(Record r, boolean show) {
- if (show) {
- if (!mExpanded) {
- mTriggeredExpand = true;
- mHost.animateToggleQSExpansion();
- } else {
- mTriggeredExpand = false;
- }
- } else if (mTriggeredExpand) {
- mHost.animateToggleQSExpansion();
- mTriggeredExpand = false;
- }
if (r instanceof TileRecord) {
handleShowDetailTile((TileRecord) r, show);
} else {
public QSTile<?> tile;
public QSTileBaseView tileView;
public boolean scanState;
+ public QSTile.Callback callback;
}
public interface Callback {
mHandler.obtainMessage(H.ADD_CALLBACK, callback).sendToTarget();
}
+ public void removeCallback(Callback callback) {
+ mHandler.obtainMessage(H.REMOVE_CALLBACK, callback).sendToTarget();
+ }
+
public void removeCallbacks() {
mHandler.sendEmptyMessage(H.REMOVE_CALLBACKS);
}
handleRefreshState(null);
}
+ private void handleRemoveCallback(Callback callback) {
+ mCallbacks.remove(callback);
+ }
+
private void handleRemoveCallbacks() {
mCallbacks.clear();
}
private static final int DESTROY = 10;
private static final int CLEAR_STATE = 11;
private static final int REMOVE_CALLBACKS = 12;
- private static final int SET_LISTENING = 13;
+ private static final int REMOVE_CALLBACK = 13;
+ private static final int SET_LISTENING = 14;
private H(Looper looper) {
super(looper);
} else if (msg.what == REMOVE_CALLBACKS) {
name = "handleRemoveCallbacks";
handleRemoveCallbacks();
+ } else if (msg.what == REMOVE_CALLBACK) {
+ name = "handleRemoveCallback";
+ handleRemoveCallback((QSTile.Callback) msg.obj);
} else if (msg.what == CLICK) {
name = "handleClick";
if (mState.disabledByPolicy) {
}
@Override
+ protected boolean shouldShowDetail() {
+ return !mExpanded;
+ }
+
+ @Override
protected void drawTile(TileRecord r, State state) {
if (state instanceof SignalState) {
State copy = r.tile.newTileState();
}
@Override
- protected void showDetail(boolean show, Record r) {
- // Do nothing, will be handled by the QSPanel.
- }
-
- @Override
protected QSTileBaseView createTileView(QSTile<?> tile, boolean collapsedView) {
return new QSTileBaseView(mContext, tile.createTileView(mContext), collapsedView);
}
private ImageView mBadgeView;
private Task mTask;
private boolean mDismissState;
+ private boolean mTouchExplorationEnabled;
private int mCornerRadius;
private ViewFocusAnimator mViewFocusAnimator;
R.dimen.recents_task_view_rounded_corners_radius);
mRecentsRowFocusAnimationHolder = new RecentsRowFocusAnimationHolder(this, mInfoFieldView);
SystemServicesProxy ssp = Recents.getSystemServices();
- if (!ssp.isTouchExplorationEnabled()) {
+ mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
+ if (!mTouchExplorationEnabled) {
mDismissIconView.setVisibility(VISIBLE);
} else {
mDismissIconView.setVisibility(GONE);
private void setDismissState(boolean dismissState) {
if (mDismissState != dismissState) {
mDismissState = dismissState;
- if (dismissState) {
- mDismissAnimationsHolder.startEnterAnimation();
- } else {
- mDismissAnimationsHolder.startExitAnimation();
+ // Check for touch exploration to ensure dismiss icon/text do not
+ // get animated. This should be removed based on decision from
+ // b/29208918
+ if (!mTouchExplorationEnabled) {
+ if (dismissState) {
+ mDismissAnimationsHolder.startEnterAnimation();
+ } else {
+ mDismissAnimationsHolder.startExitAnimation();
+ }
}
}
}
import android.util.AttributeSet;
import android.widget.RelativeLayout;
import com.android.systemui.qs.QSPanel;
+import com.android.systemui.qs.QSPanel.Callback;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.NetworkControllerImpl;
import com.android.systemui.statusbar.policy.NextAlarmController;
public abstract void setBatteryController(BatteryController batteryController);
public abstract void setNextAlarmController(NextAlarmController nextAlarmController);
public abstract void setUserInfoController(UserInfoController userInfoController);
+ public abstract void setCallback(Callback qsPanelCallback);
}
import com.android.systemui.R;
import com.android.systemui.qs.QSAnimator;
import com.android.systemui.qs.QSPanel;
+import com.android.systemui.qs.QSPanel.Callback;
import com.android.systemui.qs.QuickQSPanel;
import com.android.systemui.qs.TouchAnimator;
import com.android.systemui.statusbar.policy.BatteryController;
@Override
public void setExpanded(boolean expanded) {
mExpanded = expanded;
+ mHeaderQsPanel.setExpanded(expanded);
updateEverything();
}
}
@Override
+ public void setCallback(Callback qsPanelCallback) {
+ mHeaderQsPanel.setCallback(qsPanelCallback);
+ }
+
+ @Override
public void setEmergencyCallsOnly(boolean show) {
boolean changed = show != mShowEmergencyCallsOnly;
if (changed) {
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
import com.android.systemui.qs.QSPanel;
+import com.android.systemui.qs.QSPanel.Callback;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.QSTile.DetailAdapter;
import com.android.systemui.statusbar.policy.BatteryController;
}
@Override
+ public void setCallback(Callback qsPanelCallback) {
+ }
+
+ @Override
public void onClick(View v) {
if (v == mSettingsButton) {
if (mSettingsButton.isTunerClick()) {
synchronized(this) {
if (mActiveRestoreSession != null) {
- Slog.d(TAG, "Restore session requested but one already active");
+ Slog.i(TAG, "Restore session requested but one already active");
+ return null;
+ }
+ if (mBackupRunning) {
+ Slog.i(TAG, "Restore session requested but currently running backups");
return null;
}
mActiveRestoreSession = new ActiveRestoreSession(packageName, transport);
} catch (RemoteException e) {
Slog.e(TAG,"Unable to call onBrEdrDown", e);
} finally {
- mBluetoothLock.readLock().lock();
+ mBluetoothLock.readLock().unlock();
}
} else if (st == BluetoothAdapter.STATE_ON){
// disable without persisting the setting
for (UpdateRecord record : records) {
if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mUid))) {
LocationRequest locationRequest = record.mRequest;
+
+ // Don't assign battery blame for update records whose
+ // client has no permission to receive location data.
+ if (!providerRequest.locationRequests.contains(locationRequest)) {
+ continue;
+ }
+
if (locationRequest.getInterval() <= thresholdInterval) {
if (record.mReceiver.mWorkSource != null
&& record.mReceiver.mWorkSource.size() > 0
if (forWrite) {
match = vol.isVisibleForWrite(userId);
} else {
- match = vol.isVisibleForRead(userId) || includeInvisible;
+ match = vol.isVisibleForRead(userId)
+ || (includeInvisible && vol.getPath() != null);
}
if (!match) continue;
if (appId < 0 && packageName != null) {
try {
appId = UserHandle.getAppId(AppGlobals.getPackageManager()
- .getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, userId));
+ .getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, 0));
} catch (RemoteException e) {
}
}
args.length - opti);
}
synchronized (this) {
- dumpBroadcastStatsLocked(fd, pw, args, opti, true, dumpPackage);
+ if (dumpCheckinFormat) {
+ dumpBroadcastStatsCheckinLocked(fd, pw, args, opti, dumpCheckin,
+ dumpPackage);
+ } else {
+ dumpBroadcastStatsLocked(fd, pw, args, opti, true, dumpPackage);
+ }
}
} else if ("intents".equals(cmd) || "i".equals(cmd)) {
String[] newArgs;
// Don't debug things in the system process
if (!aInfo.processName.equals("system")) {
if ((startFlags & ActivityManager.START_FLAG_DEBUG) != 0) {
- final ProcessRecord app = mService.getProcessRecordLocked(
- aInfo.processName, aInfo.applicationInfo.uid, true);
- // If the process already started, we can't wait for debugger and shouldn't
- // modify the debug settings.
- // For non-persistent debug, normally we set the debug app here, and restores
- // to the original at attachApplication time. However, if the app process
- // already exists, we won't get another attachApplication, and the debug setting
- // never gets restored. Furthermore, if we get two such calls back-to-back,
- // both mOrigDebugApp and mDebugApp will become the same app, and it won't be
- // cleared even if we get attachApplication after the app process is killed.
- if (app == null || app.thread == null) {
- mService.setDebugApp(aInfo.processName, true, false);
- } else {
- Slog.w(TAG, "Ignoring waitForDebugger because process already exists");
- }
+ mService.setDebugApp(aInfo.processName, true, false);
}
if ((startFlags & ActivityManager.START_FLAG_NATIVE_DEBUGGING) != 0) {
private Intent mNewTaskIntent;
private ActivityStack mSourceStack;
private ActivityStack mTargetStack;
- // TODO: Is the mMoveHome flag really needed?
- private boolean mMovedHome;
+ // Indicates that we moved other task and are going to put something on top soon, so
+ // we don't want to show it redundantly or accidentally change what's shown below.
+ private boolean mMovedOtherTask;
private boolean mMovedToFront;
private boolean mNoAnimation;
private boolean mKeepCurTransition;
mSourceStack = null;
mTargetStack = null;
- mMovedHome = false;
+ mMovedOtherTask = false;
mMovedToFront = false;
mNoAnimation = false;
mKeepCurTransition = false;
resumeTargetStackIfNeeded();
return START_RETURN_INTENT_TO_CALLER;
}
-
setTaskFromIntentActivity(mReusedActivity);
if (!mAddingToTask && mReuseTask == null) {
Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
return START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
- if (!mMovedHome) {
+ if (!mMovedOtherTask) {
updateTaskReturnToType(mStartActivity.task, mLaunchFlags, topStack);
}
} else if (mSourceRecord != null) {
if (mLaunchTaskBehind && mSourceRecord != null) {
intentActivity.setTaskToAffiliateWith(mSourceRecord.task);
}
- mMovedHome = true;
+ mMovedOtherTask = true;
// If the launch flags carry both NEW_TASK and CLEAR_TASK, the task's activities
// will be cleared soon by ActivityStarter in setTaskFromIntentActivity().
mReuseTask = intentActivity.task;
mReuseTask.performClearTaskLocked();
mReuseTask.setIntent(mStartActivity);
+ // When we clear the task - focus will be adjusted, which will bring another task
+ // to top before we launch the activity we need. This will temporary swap their
+ // mTaskToReturnTo values and we don't want to overwrite them accidentally.
+ mMovedOtherTask = true;
} else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
|| mLaunchSingleInstance || mLaunchSingleTask) {
ActivityRecord top = intentActivity.task.performClearTaskLocked(mStartActivity,
for (int curUser : users) {
long timeout = SystemClock.uptimeMillis() + 5000;
synchronized (conn) {
- long now = SystemClock.uptimeMillis();
- while (conn.mContainerService == null && now < timeout) {
+ long now;
+ while (conn.mContainerService == null &&
+ (now = SystemClock.uptimeMillis()) < timeout) {
try {
conn.wait(timeout - now);
} catch (InterruptedException e) {
private static final String RESTRICTIONS_FILE_PREFIX = "res_";
private static final String XML_SUFFIX = ".xml";
+ private static final int ALLOWED_FLAGS_FOR_CREATE_USERS_PERMISSION =
+ UserInfo.FLAG_MANAGED_PROFILE
+ | UserInfo.FLAG_EPHEMERAL
+ | UserInfo.FLAG_RESTRICTED
+ | UserInfo.FLAG_GUEST;
+
private static final int MIN_USER_ID = 10;
// We need to keep process uid within Integer.MAX_VALUE.
private static final int MAX_USER_ID = Integer.MAX_VALUE / UserHandle.PER_USER_RANGE;
@Override
public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
- checkManageUsersPermission("query users");
+ checkManageOrCreateUsersPermission("query users");
synchronized (mUsersLock) {
ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
final int userSize = mUsers.size();
}
/**
+ * Enforces that only the system UID or root's UID or apps that have the
+ * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} or
+ * {@link android.Manifest.permission#CREATE_USERS CREATE_USERS}
+ * can make certain calls to the UserManager.
+ *
+ * @param message used as message if SecurityException is thrown
+ * @throws SecurityException if the caller is not system or root
+ * @see #hasManageOrCreateUsersPermission()
+ */
+ private static final void checkManageOrCreateUsersPermission(String message) {
+ if (!hasManageOrCreateUsersPermission()) {
+ throw new SecurityException(
+ "You either need MANAGE_USERS or CREATE_USERS permission to: " + message);
+ }
+ }
+
+ /**
+ * Similar to {@link #checkManageOrCreateUsersPermission(String)} but when the caller is tries
+ * to create user/profiles other than what is allowed for
+ * {@link android.Manifest.permission#CREATE_USERS CREATE_USERS} permission, then it will only
+ * allow callers with {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} permission.
+ */
+ private static final void checkManageOrCreateUsersPermission(int creationFlags) {
+ if ((creationFlags & ~ALLOWED_FLAGS_FOR_CREATE_USERS_PERMISSION) == 0) {
+ if (!hasManageOrCreateUsersPermission()) {
+ throw new SecurityException("You either need MANAGE_USERS or CREATE_USERS "
+ + "permission to create an user with flags: " + creationFlags);
+ }
+ } else if (!hasManageUsersPermission()) {
+ throw new SecurityException("You need MANAGE_USERS permission to create an user "
+ + " with flags: " + creationFlags);
+ }
+ }
+
+ /**
* @return whether the calling UID is system UID or root's UID or the calling app has the
* {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS}.
*/
}
/**
+ * @return whether the calling UID is system UID or root's UID or the calling app has the
+ * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} or
+ * {@link android.Manifest.permission#CREATE_USERS CREATE_USERS}.
+ */
+ private static final boolean hasManageOrCreateUsersPermission() {
+ final int callingUid = Binder.getCallingUid();
+ return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)
+ || callingUid == Process.ROOT_UID
+ || ActivityManager.checkComponentPermission(
+ android.Manifest.permission.MANAGE_USERS,
+ callingUid, -1, true) == PackageManager.PERMISSION_GRANTED
+ || ActivityManager.checkComponentPermission(
+ android.Manifest.permission.CREATE_USERS,
+ callingUid, -1, true) == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
* Enforces that only the system UID or root's UID (on any user) can make certain calls to the
* UserManager.
*
@Override
public UserInfo createProfileForUser(String name, int flags, int userId) {
- checkManageUsersPermission("Only the system can create users");
+ checkManageOrCreateUsersPermission(flags);
return createUserInternal(name, flags, userId);
}
@Override
public UserInfo createUser(String name, int flags) {
- checkManageUsersPermission("Only the system can create users");
+ checkManageOrCreateUsersPermission(flags);
return createUserInternal(name, flags, UserHandle.USER_NULL);
}
*/
@Override
public boolean removeUser(int userHandle) {
- checkManageUsersPermission("Only the system can remove users");
+ checkManageOrCreateUsersPermission("Only the system can remove users");
if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(
UserManager.DISALLOW_REMOVE_USER, false)) {
Log.w(LOG_TAG, "Cannot remove user. DISALLOW_REMOVE_USER is enabled.");
final PhoneWindow win = new PhoneWindow(context);
win.setIsStartingWindow(true);
- final Resources r = context.getResources();
- win.setTitle(r.getText(labelRes, nonLocalizedLabel));
+ CharSequence label = context.getResources().getText(labelRes, null);
+ // Only change the accessibility title if the label is localized
+ if (label != null) {
+ win.setTitle(label, true);
+ } else {
+ win.setTitle(nonLocalizedLabel, false);
+ }
win.setType(
WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
window.layer = windowState.mLayer;
window.token = windowState.mClient.asBinder();
window.title = windowState.mAttrs.accessibilityTitle;
- if (window.title == null) {
- window.title = windowState.mAttrs.getTitle();
- }
window.accessibilityIdOfAnchor = windowState.mAttrs.accessibilityIdOfAnchor;
WindowState attachedWindow = windowState.mAttachedWindow;
mTmpSize.bottom = mTmpSize.top + 1;
}
- final int displayId = w.getDisplayId();
- float scale = 1.0f;
- // Magnification is supported only for the default display.
- if (mService.mAccessibilityController != null && displayId == DEFAULT_DISPLAY) {
- final MagnificationSpec spec =
- mService.mAccessibilityController.getMagnificationSpecForWindowLocked(w);
- if (spec != null && !spec.isNop()) {
- scale = spec.scale;
- }
- }
-
// Adjust for surface insets.
- mTmpSize.left -= scale * attrs.surfaceInsets.left;
- mTmpSize.top -= scale * attrs.surfaceInsets.top;
- mTmpSize.right += scale * attrs.surfaceInsets.right;
- mTmpSize.bottom += scale * attrs.surfaceInsets.bottom;
+ mTmpSize.left -= attrs.surfaceInsets.left;
+ mTmpSize.top -= attrs.surfaceInsets.top;
+ mTmpSize.right += attrs.surfaceInsets.right;
+ mTmpSize.bottom += attrs.surfaceInsets.bottom;
}
boolean hasSurface() {
void updateSurfaceWindowCrop(Rect clipRect, Rect finalClipRect, boolean recoveringMemory) {
if (DEBUG_WINDOW_CROP) Slog.d(TAG, "updateSurfaceWindowCrop: win=" + mWin
+ " clipRect=" + clipRect + " finalClipRect=" + finalClipRect);
- if (!clipRect.equals(mLastClipRect)) {
- mLastClipRect.set(clipRect);
- mSurfaceController.setCropInTransaction(clipRect, recoveringMemory);
+ if (clipRect != null) {
+ if (!clipRect.equals(mLastClipRect)) {
+ mLastClipRect.set(clipRect);
+ mSurfaceController.setCropInTransaction(clipRect, recoveringMemory);
+ }
+ } else {
+ mSurfaceController.clearCropInTransaction(recoveringMemory);
}
if (!finalClipRect.equals(mLastFinalClipRect)) {
mLastFinalClipRect.set(finalClipRect);
}
private int resolveStackClip() {
-
// App animation overrides window animation stack clip mode.
if (mAppAnimator != null && mAppAnimator.animation != null) {
return mAppAnimator.getStackClip();
// aren't observing known issues here outside of PiP resizing. (Typically
// the other windows that use -1 are PopupWindows which aren't likely
// to be rendering while we resize).
+
+ boolean wasForceScaled = mForceScaleUntilResize;
+
if (!w.inPinnedWorkspace() || (!w.mRelayoutCalled || w.mInRelayout)) {
mSurfaceResized = mSurfaceController.setSizeInTransaction(
mTmpSize.width(), mTmpSize.height(), recoveringMemory);
}
mForceScaleUntilResize = mForceScaleUntilResize && !mSurfaceResized;
-
calculateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect);
+
+ float surfaceWidth = mSurfaceController.getWidth();
+ float surfaceHeight = mSurfaceController.getHeight();
+
if ((task != null && task.mStack.getForceScaleToCrop()) || mForceScaleUntilResize) {
int hInsets = w.getAttrs().surfaceInsets.left + w.getAttrs().surfaceInsets.right;
int vInsets = w.getAttrs().surfaceInsets.top + w.getAttrs().surfaceInsets.bottom;
- float surfaceWidth = mSurfaceController.getWidth();
- float surfaceHeight = mSurfaceController.getHeight();
+ if (!mForceScaleUntilResize) {
+ mSurfaceController.forceScaleableInTransaction(true);
+ }
// We want to calculate the scaling based on the content area, not based on
// the entire surface, so that we scale in sync with windows that don't have insets.
mExtraHScale = (mTmpClipRect.width() - hInsets) / (float)(surfaceWidth - hInsets);
posX += w.getAttrs().surfaceInsets.left * (1 - mExtraHScale);
posY += w.getAttrs().surfaceInsets.top * (1 - mExtraVScale);
- mSurfaceController.setPositionInTransaction(posX, posY, recoveringMemory);
+ mSurfaceController.setPositionInTransaction((float)Math.floor(posX),
+ (float)Math.floor(posY), recoveringMemory);
// Since we are scaled to fit in our previously desired crop, we can now
// expose the whole window in buffer space, and not risk extending
// We need to ensure for each surface, that we disable transformation matrix
// scaling in the same transaction which we resize the surface in.
// As we are in SCALING_MODE_SCALE_TO_WINDOW, SurfaceFlinger will
- // then take over the scaling until the new buffer arrives, and things
+ // then take over the scaling until the new buffer arrives, and things
// will be seamless.
mForceScaleUntilResize = true;
} else {
recoveringMemory);
}
- updateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect, recoveringMemory);
+ // If we are ending the scaling mode. We switch to SCALING_MODE_FREEZE
+ // to prevent further updates until buffer latch. Normally position
+ // would continue to apply immediately. But we need a different position
+ // before and after resize (since we have scaled the shadows, as discussed
+ // above).
+ if (wasForceScaled && !mForceScaleUntilResize) {
+ mSurfaceController.setPositionAppliesWithResizeInTransaction(true);
+ mSurfaceController.forceScaleableInTransaction(false);
+ }
+
+ Rect clipRect = mTmpClipRect;
+ if (w.inPinnedWorkspace()) {
+ clipRect = null;
+ task.mStack.getDimBounds(mTmpFinalClipRect);
+ mTmpFinalClipRect.inset(-w.mAttrs.surfaceInsets.left, -w.mAttrs.surfaceInsets.top,
+ -w.mAttrs.surfaceInsets.right, -w.mAttrs.surfaceInsets.bottom);
+ }
+
+ updateSurfaceWindowCrop(clipRect, mTmpFinalClipRect, recoveringMemory);
mSurfaceController.setMatrixInTransaction(mDsDx * w.mHScale * mExtraHScale,
mDtDx * w.mVScale * mExtraVScale,
import static android.view.Surface.SCALING_MODE_FREEZE;
import static android.view.Surface.SCALING_MODE_SCALE_TO_WINDOW;
+import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
title = name;
- if (DEBUG_SURFACE_TRACE) {
+ // For opaque child windows placed under parent windows,
+ // we use a special SurfaceControl which mirrors commands
+ // to a black-out layer placed one Z-layer below the surface.
+ // This prevents holes to whatever app/wallpaper is underneath.
+ if (animator.mWin.isChildWindow() &&
+ animator.mWin.mSubLayer < 0) {
+ mSurfaceControl = new SurfaceControlWithBackground(s,
+ name, w, h, format, flags);
+ } else if (DEBUG_SURFACE_TRACE) {
mSurfaceControl = new SurfaceTrace(
s, name, w, h, format, flags);
} else {
}
}
+ void clearCropInTransaction(boolean recoveringMemory) {
+ if (SHOW_TRANSACTIONS) logSurface(
+ "CLEAR CROP", null);
+ try {
+ Rect clipRect = new Rect(0, 0, -1, -1);
+ mSurfaceControl.setWindowCrop(clipRect);
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Error setting clearing crop of " + this, e);
+ if (!recoveringMemory) {
+ mAnimator.reclaimSomeSurfaceMemory("crop", true);
+ }
+ }
+ }
+
void setFinalCropInTransaction(Rect clipRect) {
if (SHOW_TRANSACTIONS) logSurface(
"FINAL CROP " + clipRect.toShortString(), null);
}
}
+ void setPositionAppliesWithResizeInTransaction(boolean recoveringMemory) {
+ mSurfaceControl.setPositionAppliesWithResize();
+ }
+
void setMatrixInTransaction(float dsdx, float dtdx, float dsdy, float dtdy,
boolean recoveringMemory) {
try {
}
@Override
+ public void setPositionAppliesWithResize() {
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setPositionAppliesWithResize(): OLD: "
+ + this + ". Called by" + Debug.getCallers(9));
+ super.setPositionAppliesWithResize();
+ }
+
+ @Override
public void setSize(int w, int h) {
if (w != mSize.x || h != mSize.y) {
if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setSize(" + w + "," + h + "): OLD:"
+ " (" + mDsdx + "," + mDtdx + "," + mDsdy + "," + mDtdy + ")";
}
}
+
+ private static class SurfaceControlWithBackground extends SurfaceControl {
+ private SurfaceControl mBackgroundControl;
+ private boolean mOpaque = true;
+ private boolean mVisible = false;
+
+ public SurfaceControlWithBackground(SurfaceSession s,
+ String name, int w, int h, int format, int flags)
+ throws OutOfResourcesException {
+ super(s, name, w, h, format, flags);
+ mBackgroundControl = new SurfaceControl(s, name, w, h,
+ PixelFormat.OPAQUE, flags | SurfaceControl.FX_SURFACE_DIM);
+ mOpaque = (flags & SurfaceControl.OPAQUE) != 0;
+ }
+
+ @Override
+ public void setAlpha(float alpha) {
+ super.setAlpha(alpha);
+ mBackgroundControl.setAlpha(alpha);
+ }
+
+ @Override
+ public void setLayer(int zorder) {
+ super.setLayer(zorder);
+ mBackgroundControl.setLayer(zorder - 1);
+ }
+
+ @Override
+ public void setPosition(float x, float y) {
+ super.setPosition(x, y);
+ mBackgroundControl.setPosition(x, y);
+ }
+
+ @Override
+ public void setSize(int w, int h) {
+ super.setSize(w, h);
+ mBackgroundControl.setSize(w, h);
+ }
+
+ @Override
+ public void setWindowCrop(Rect crop) {
+ super.setWindowCrop(crop);
+ mBackgroundControl.setWindowCrop(crop);
+ }
+
+ @Override
+ public void setFinalCrop(Rect crop) {
+ super.setFinalCrop(crop);
+ mBackgroundControl.setFinalCrop(crop);
+ }
+
+ @Override
+ public void setLayerStack(int layerStack) {
+ super.setLayerStack(layerStack);
+ mBackgroundControl.setLayerStack(layerStack);
+ }
+
+ @Override
+ public void setOpaque(boolean isOpaque) {
+ super.setOpaque(isOpaque);
+ mOpaque = isOpaque;
+ updateBackgroundVisibility();
+ }
+
+ @Override
+ public void setSecure(boolean isSecure) {
+ super.setSecure(isSecure);
+ }
+
+ @Override
+ public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
+ super.setMatrix(dsdx, dtdx, dsdy, dtdy);
+ mBackgroundControl.setMatrix(dsdx, dtdx, dsdy, dtdy);
+ }
+
+ @Override
+ public void hide() {
+ mVisible = false;
+ super.hide();
+ updateBackgroundVisibility();
+ }
+
+ @Override
+ public void show() {
+ mVisible = true;
+ super.show();
+ updateBackgroundVisibility();
+ }
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ mBackgroundControl.destroy();
+ }
+
+ @Override
+ public void release() {
+ super.release();
+ mBackgroundControl.release();
+ }
+
+ @Override
+ public void setTransparentRegionHint(Region region) {
+ super.setTransparentRegionHint(region);
+ mBackgroundControl.setTransparentRegionHint(region);
+ }
+
+ @Override
+ public void deferTransactionUntil(IBinder handle, long frame) {
+ super.deferTransactionUntil(handle, frame);
+ mBackgroundControl.deferTransactionUntil(handle, frame);
+ }
+
+ private void updateBackgroundVisibility() {
+ if (mOpaque && mVisible) {
+ mBackgroundControl.show();
+ } else {
+ mBackgroundControl.hide();
+ }
+ }
+ }
}