summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/Activity.java2
-rw-r--r--core/java/android/app/ActivityThread.java16
-rw-r--r--core/java/android/text/SpannableStringBuilder.java7
-rw-r--r--core/java/android/view/accessibility/AccessibilityWindowInfo.java4
-rw-r--r--core/java/com/android/internal/policy/PhoneWindow.java14
-rw-r--r--core/java/com/android/internal/util/FastPrintWriter.java2
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java17
-rw-r--r--docs/html/distribute/index.jd4
-rw-r--r--docs/html/preview/features/data-saver.jd30
-rw-r--r--docs/html/preview/index.jd2
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml2
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java34
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java21
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java9
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java150
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java3
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java10
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java3
18 files changed, 229 insertions, 101 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index ac5f3effa1f9..a5fcec622956 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5952,7 +5952,7 @@ public class Activity extends ContextThemeWrapper
return false;
}
try {
- return ActivityManagerNative.getDefault().isTopOfTask(mToken);
+ return ActivityManagerNative.getDefault().isTopOfTask(getActivityToken());
} catch (RemoteException e) {
return false;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 7198146bee0f..b220b2e23732 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1166,12 +1166,22 @@ public final class ActivityThread {
@Override
public void dumpDbInfo(final FileDescriptor fd, final String[] args) {
if (mSystemThread) {
- // Ensure this invocation is asynchronous to prevent
- // writer waiting due to buffer cannot be consumed.
+ // Ensure this invocation is asynchronous to prevent writer waiting if buffer cannot
+ // be consumed. But it must duplicate the file descriptor first, since caller might
+ // be closing it.
+ final ParcelFileDescriptor dup;
+ try {
+ dup = ParcelFileDescriptor.dup(fd);
+ } catch (IOException e) {
+ Log.w(TAG, "Could not dup FD " + fd.getInt$());
+ return;
+ }
+
AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
@Override
public void run() {
- dumpDatabaseInfo(fd, args);
+ dumpDatabaseInfo(dup.getFileDescriptor(), args);
+ IoUtils.closeQuietly(dup);
}
});
} else {
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index 787202eded02..dc8e4b93e093 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -19,7 +19,6 @@ package android.text;
import android.annotation.Nullable;
import android.graphics.Canvas;
import android.graphics.Paint;
-import android.text.style.ParagraphStyle;
import android.util.Log;
import com.android.internal.util.ArrayUtils;
@@ -554,7 +553,8 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
if (adjustSelection) {
boolean changed = false;
if (selectionStart > start && selectionStart < end) {
- final int offset = (selectionStart - start) * newLen / origLen;
+ final long diff = selectionStart - start;
+ final int offset = Math.toIntExact(diff * newLen / origLen);
selectionStart = start + offset;
changed = true;
@@ -562,7 +562,8 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
Spanned.SPAN_POINT_POINT);
}
if (selectionEnd > start && selectionEnd < end) {
- final int offset = (selectionEnd - start) * newLen / origLen;
+ final long diff = selectionEnd - start;
+ final int offset = Math.toIntExact(diff * newLen / origLen);
selectionEnd = start + offset;
changed = true;
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index 52f35de7e189..d0d4507e7b62 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -16,7 +16,6 @@
package android.view.accessibility;
-import android.annotation.Nullable;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
@@ -102,9 +101,8 @@ public final class AccessibilityWindowInfo implements Parcelable {
/**
* Gets the title of the window.
*
- * @return The title of the window, or {@code null} if none is available.
+ * @return The title.
*/
- @Nullable
public CharSequence getTitle() {
return mTitle;
}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 9ad750d3a599..18408aa72f5f 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -528,22 +528,16 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
@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;
- if (updateAccessibilityTitle) {
- WindowManager.LayoutParams params = getAttributes();
- if (!TextUtils.equals(title, params.accessibilityTitle)) {
- params.accessibilityTitle = TextUtils.stringOrSpannedString(title);
- dispatchWindowAttributesChanged(getAttributes());
- }
+ WindowManager.LayoutParams params = getAttributes();
+ if (!TextUtils.equals(title, params.accessibilityTitle)) {
+ params.accessibilityTitle = TextUtils.stringOrSpannedString(title);
+ dispatchWindowAttributesChanged(getAttributes());
}
}
diff --git a/core/java/com/android/internal/util/FastPrintWriter.java b/core/java/com/android/internal/util/FastPrintWriter.java
index e46e6b026fd9..dc3832a1713d 100644
--- a/core/java/com/android/internal/util/FastPrintWriter.java
+++ b/core/java/com/android/internal/util/FastPrintWriter.java
@@ -345,7 +345,7 @@ public class FastPrintWriter extends PrintWriter {
if (mOutputStream != null) {
CharBuffer charBuffer = CharBuffer.wrap(mText, 0, mPos);
CoderResult result = mCharset.encode(charBuffer, mBytes, true);
- while (true) {
+ while (!mIoError) {
if (result.isError()) {
throw new IOException(result.toString());
} else if (result.isOverflow()) {
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 1beea8c11386..2e0dfa5fa731 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -916,23 +916,6 @@ public class LockPatternUtils {
}
/**
- * Gets whether the device is encrypted.
- *
- * @return Whether the device is encrypted.
- */
- public static boolean isDeviceEncrypted() {
- IMountService mountService = IMountService.Stub.asInterface(
- ServiceManager.getService("mount"));
- try {
- return mountService.getEncryptionState() != IMountService.ENCRYPTION_STATE_NONE
- && mountService.getPasswordType() != StorageManager.CRYPT_TYPE_DEFAULT;
- } catch (RemoteException re) {
- Log.e(TAG, "Error getting encryption state", re);
- }
- return true;
- }
-
- /**
* Determine if the device supports encryption, even if it's set to default. This
* differs from isDeviceEncrypted() in that it returns true even if the device is
* encrypted with the default password.
diff --git a/docs/html/distribute/index.jd b/docs/html/distribute/index.jd
index 638477302d0a..424983d4d286 100644
--- a/docs/html/distribute/index.jd
+++ b/docs/html/distribute/index.jd
@@ -67,7 +67,9 @@ page.metaDescription=The most visited store in the world for Android apps. Cloud
</li>
<li class="dac-tab-view" data-tab-view="type">
+ <!--
<div class="dac-filter-item" data-filter-value="type:distribute" data-filter-name="Articles"></div>
+ -->
<div class="dac-filter-item" data-filter-value="type:youtube" data-filter-name="Videos"></div>
<div class="dac-filter-item" data-filter-value="type:blog" data-filter-name="Blog posts"></div>
</li>
@@ -78,7 +80,7 @@ page.metaDescription=The most visited store in the world for Android apps. Cloud
</div>
<div class="resource-flow-layout col-16" id="latest-resources"
- data-query="type:distribute, type:youtube+tag:googleplay, type:blog+tag:googleplay+tag:featured"
+ data-query="type:youtube+tag:googleplay, type:blog+tag:googleplay+tag:featured"
data-sortOrder="-timestamp"
data-cardSizes="6x6"
data-items-per-page="6"
diff --git a/docs/html/preview/features/data-saver.jd b/docs/html/preview/features/data-saver.jd
index 761556e1ead6..abc47eaa3837 100644
--- a/docs/html/preview/features/data-saver.jd
+++ b/docs/html/preview/features/data-saver.jd
@@ -179,10 +179,13 @@ if (connMgr.isActiveNetworkMetered()) {
Testing with Android Debug Bridge Commands
</h2>
-The <a href="{@docRoot}tools/help/adb.html">Android Debug Bridge (ADB)</a>
-provides a few commands that you can use to check and configure network
-permissions:
-
+<p>
+ The <a href="{@docRoot}tools/help/adb.html">Android Debug Bridge (ADB)</a>
+ provides a few commands that you can use to test your app in Data Saver
+ conditions. You can check and configure network
+ permissions or set wireless networks as metered to test your app on unmetered
+ networks.
+</p>
<dl>
<dt>
<code>$ adb shell dumpsys netpolicy</code>
@@ -231,4 +234,23 @@ permissions:
Removes the specified package UID from the whitelist to block background
metered data usage while Data Saver is enabled.
</dd>
+
+ <dt>
+ <code>$ adb shell cmd netpolicy list wifi-networks</code>
+ </dt>
+
+ <dd>
+ Lists all wifi networks, displaying whether they're metered.
+ </dd>
+
+
+ <dt>
+ <code>$ adb shell cmd netpolicy set metered-network &lt;WIFI_SSID&gt;
+ true</code>
+ </dt>
+
+ <dd>
+ Sets wifi with the specified SSID as metered, allowing you to simulate a
+ metered network on an unmetered network.
+ </dd>
</dl> \ No newline at end of file
diff --git a/docs/html/preview/index.jd b/docs/html/preview/index.jd
index b0631add8b13..918de48c41d3 100644
--- a/docs/html/preview/index.jd
+++ b/docs/html/preview/index.jd
@@ -57,7 +57,7 @@ footer.hide=1
<div id="useUpdatedTemplates" style="display:none" class="dac-section dac-slim dac-gray dac-expand">
<div class="wrap dac-offset-parent">
- <a class="dac-fab dac-scroll-button" data-scroll-button href="#build-apps">
+ <a class="dac-fab dac-scroll-button" data-scroll-button href="#latest">
<i class="dac-sprite dac-arrow-down-gray"></i>
</a>
<ul class="dac-actions">
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 6a57c134d769..cd08f34aa6a5 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -580,7 +580,7 @@
<string name="accessibility_data_saver_on" msgid="8454111686783887148">"Reducir datos está activada"</string>
<string name="accessibility_data_saver_off" msgid="8841582529453005337">"Reducir datos está desactivada"</string>
<string name="switch_bar_on" msgid="1142437840752794229">"Activado"</string>
- <string name="switch_bar_off" msgid="8803270596930432874">"Desactivar"</string>
+ <string name="switch_bar_off" msgid="8803270596930432874">"Desactivado"</string>
<string name="nav_bar" msgid="1993221402773877607">"Barra de navegación"</string>
<string name="start" msgid="6873794757232879664">"Iniciar"</string>
<string name="center" msgid="4327473927066010960">"Centro"</string>
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 6a0d48872a6b..acf8009e34a6 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3388,7 +3388,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
* was no always-on VPN to start. {@code false} otherwise.
*/
private boolean startAlwaysOnVpn(int userId) {
- final String alwaysOnPackage;
synchronized (mVpns) {
Vpn vpn = mVpns.get(userId);
if (vpn == null) {
@@ -3397,27 +3396,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
Slog.wtf(TAG, "User " + userId + " has no Vpn configuration");
return false;
}
- alwaysOnPackage = vpn.getAlwaysOnPackage();
- // Skip if there is no service to start.
- if (alwaysOnPackage == null) {
- return true;
- }
- // Skip if the service is already established. This isn't bulletproof: it's not bound
- // until after establish(), so if it's mid-setup onStartCommand will be sent twice,
- // which may restart the connection.
- if (vpn.getNetworkInfo().isConnected()) {
- return true;
- }
- }
- // Start the VPN service declared in the app's manifest.
- Intent serviceIntent = new Intent(VpnConfig.SERVICE_INTERFACE);
- serviceIntent.setPackage(alwaysOnPackage);
- try {
- return mContext.startServiceAsUser(serviceIntent, UserHandle.of(userId)) != null;
- } catch (RuntimeException e) {
- Slog.w(TAG, "VpnService " + serviceIntent + " failed to start", e);
- return false;
+ return vpn.startAlwaysOnVpn();
}
}
@@ -3449,17 +3429,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
return false;
}
- // Save the configuration
- final long token = Binder.clearCallingIdentity();
- try {
- final ContentResolver cr = mContext.getContentResolver();
- Settings.Secure.putStringForUser(cr, Settings.Secure.ALWAYS_ON_VPN_APP,
- packageName, userId);
- Settings.Secure.putIntForUser(cr, Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN,
- (lockdown ? 1 : 0), userId);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ vpn.saveAlwaysOnPackage();
}
return true;
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 4439a24a4b07..d83a750ccbb3 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -3764,7 +3764,8 @@ final class ActivityStack {
r.finishLaunchTickingLocked();
}
- private void removeActivityFromHistoryLocked(ActivityRecord r, String reason) {
+ private void removeActivityFromHistoryLocked(
+ ActivityRecord r, TaskRecord oldTop, String reason) {
mStackSupervisor.removeChildActivityContainers(r);
finishActivityResultsLocked(r, Activity.RESULT_CANCELED, null);
r.makeFinishingLocked();
@@ -3783,10 +3784,11 @@ final class ActivityStack {
validateAppTokensLocked();
}
final TaskRecord task = r.task;
+ final TaskRecord topTask = oldTop != null ? oldTop : topTask();
if (task != null && task.removeActivity(r)) {
if (DEBUG_STACK) Slog.i(TAG_STACK,
"removeActivityFromHistoryLocked: last activity removed from " + this);
- if (mStackSupervisor.isFocusedStack(this) && task == topTask() &&
+ if (mStackSupervisor.isFocusedStack(this) && task == topTask &&
task.isOverHomeStack()) {
mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo(), reason);
}
@@ -3922,6 +3924,12 @@ final class ActivityStack {
boolean removedFromHistory = false;
+ // If the activity is finishing, it's no longer considered in topRunningActivityLocked,
+ // and cleanUpActivityLocked() may change focus to another activity (or task).
+ // Get the current top task now, as removeActivityFromHistoryLocked() below need this
+ // to decide whether to return to home stack after removal.
+ final TaskRecord topTask = topTask();
+
cleanUpActivityLocked(r, false, false);
final boolean hadApp = r.app != null;
@@ -3956,7 +3964,8 @@ final class ActivityStack {
// up.
//Slog.w(TAG, "Exception thrown during finish", e);
if (r.finishing) {
- removeActivityFromHistoryLocked(r, reason + " exceptionInScheduleDestroy");
+ removeActivityFromHistoryLocked(
+ r, topTask, reason + " exceptionInScheduleDestroy");
removedFromHistory = true;
skipDestroy = true;
}
@@ -3987,7 +3996,7 @@ final class ActivityStack {
} else {
// remove this record from the history.
if (r.finishing) {
- removeActivityFromHistoryLocked(r, reason + " hadNoApp");
+ removeActivityFromHistoryLocked(r, topTask, reason + " hadNoApp");
removedFromHistory = true;
} else {
if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYED: " + r + " (no app)");
@@ -4018,7 +4027,7 @@ final class ActivityStack {
if (isInStackLocked(r) != null) {
if (r.state == ActivityState.DESTROYING) {
cleanUpActivityLocked(r, true, false);
- removeActivityFromHistoryLocked(r, reason);
+ removeActivityFromHistoryLocked(r, null, reason);
}
}
mStackSupervisor.resumeFocusedStackTopActivityLocked();
@@ -4176,7 +4185,7 @@ final class ActivityStack {
}
cleanUpActivityLocked(r, true, true);
if (remove) {
- removeActivityFromHistoryLocked(r, "appDied");
+ removeActivityFromHistoryLocked(r, null, "appDied");
}
}
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 8932e4bbafc4..c84aaacd8aff 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1537,6 +1537,11 @@ final class TaskRecord {
? Configuration.ORIENTATION_PORTRAIT
: Configuration.ORIENTATION_LANDSCAPE;
+ // Always set fontScale to be euqal to global. Can't set to 0, as that makes the override
+ // config not equal to EMPTY. Also can't set to 1, as Configuration.updateFrom will use
+ // the override scale as long as it's non-zero, and we'll always use 1.
+ config.fontScale = serviceConfig.fontScale;
+
// For calculating screen layout, we need to use the non-decor inset screen area for the
// calculation for compatibility reasons, i.e. screen area without system bars that could
// never go away in Honeycomb.
@@ -1564,6 +1569,7 @@ final class TaskRecord {
extracted.smallestScreenWidthDp = config.smallestScreenWidthDp;
extracted.orientation = config.orientation;
extracted.screenLayout = config.screenLayout;
+ extracted.fontScale = config.fontScale;
return extracted;
}
@@ -1597,6 +1603,9 @@ final class TaskRecord {
newScreenLayout = (newScreenLayout & ~SCREENLAYOUT_SIZE_MASK)
| (overrideScreenLayout & SCREENLAYOUT_SIZE_MASK);
mOverrideConfig.screenLayout = newScreenLayout;
+ // we never override the fontScale, however we need to copy over the global value
+ // so that the default 1.0 doesn't get applied as an override.
+ mOverrideConfig.fontScale = globalConfig.fontScale;
}
static Rect validateBounds(Rect bounds) {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 1bdb48a23d9d..ebacc71515bb 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -30,6 +30,7 @@ import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -53,6 +54,7 @@ import android.net.NetworkInfo.DetailedState;
import android.net.NetworkMisc;
import android.net.RouteInfo;
import android.net.UidRange;
+import android.net.Uri;
import android.os.Binder;
import android.os.FileUtils;
import android.os.IBinder;
@@ -60,12 +62,14 @@ import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
+import android.os.PatternMatcher;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemService;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.security.Credentials;
import android.security.KeyStore;
import android.text.TextUtils;
@@ -163,6 +167,45 @@ public class Vpn {
// Handle of user initiating VPN.
private final int mUserHandle;
+ // Listen to package remove and change event in this user
+ private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final Uri data = intent.getData();
+ final String packageName = data == null ? null : data.getSchemeSpecificPart();
+ if (packageName == null) {
+ return;
+ }
+
+ synchronized (Vpn.this) {
+ // Avoid race that always-on package has been unset
+ if (!packageName.equals(getAlwaysOnPackage())) {
+ return;
+ }
+
+ final String action = intent.getAction();
+ Log.i(TAG, "Received broadcast " + action + " for always-on package " + packageName
+ + " in user " + mUserHandle);
+
+ switch(action) {
+ case Intent.ACTION_PACKAGE_REPLACED:
+ // Start vpn after app upgrade
+ startAlwaysOnVpn();
+ break;
+ case Intent.ACTION_PACKAGE_REMOVED:
+ final boolean isPackageRemoved = !intent.getBooleanExtra(
+ Intent.EXTRA_REPLACING, false);
+ if (isPackageRemoved) {
+ setAndSaveAlwaysOnPackage(null, false);
+ }
+ break;
+ }
+ }
+ }
+ };
+
+ private boolean mIsPackageIntentReceiverRegistered = false;
+
public Vpn(Looper looper, Context context, INetworkManagementService netService,
int userHandle) {
mContext = context;
@@ -233,10 +276,37 @@ public class Vpn {
mAlwaysOn = (packageName != null);
mLockdown = (mAlwaysOn && lockdown);
+ maybeRegisterPackageChangeReceiverLocked(packageName);
setVpnForcedLocked(mLockdown);
return true;
}
+ private void unregisterPackageChangeReceiverLocked() {
+ // register previous intent filter
+ if (mIsPackageIntentReceiverRegistered) {
+ mContext.unregisterReceiver(mPackageIntentReceiver);
+ mIsPackageIntentReceiverRegistered = false;
+ }
+ }
+
+ private void maybeRegisterPackageChangeReceiverLocked(String packageName) {
+ // Unregister IntentFilter listening for previous always-on package change
+ unregisterPackageChangeReceiverLocked();
+
+ if (packageName != null) {
+ mIsPackageIntentReceiverRegistered = true;
+
+ IntentFilter intentFilter = new IntentFilter();
+ // Protected intent can only be sent by system. No permission required in register.
+ intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ intentFilter.addDataScheme("package");
+ intentFilter.addDataSchemeSpecificPart(packageName, PatternMatcher.PATTERN_LITERAL);
+ mContext.registerReceiverAsUser(
+ mPackageIntentReceiver, UserHandle.of(mUserHandle), intentFilter, null, null);
+ }
+ }
+
/**
* @return the package name of the VPN controller responsible for always-on VPN,
* or {@code null} if none is set or always-on VPN is controlled through
@@ -249,6 +319,69 @@ public class Vpn {
}
/**
+ * Save the always-on package and lockdown config into Settings.Secure
+ */
+ public synchronized void saveAlwaysOnPackage() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final ContentResolver cr = mContext.getContentResolver();
+ Settings.Secure.putStringForUser(cr, Settings.Secure.ALWAYS_ON_VPN_APP,
+ getAlwaysOnPackage(), mUserHandle);
+ Settings.Secure.putIntForUser(cr, Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN,
+ (mLockdown ? 1 : 0), mUserHandle);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Set and save always-on package and lockdown config
+ * @see Vpn#setAlwaysOnPackage(String, boolean)
+ * @see Vpn#saveAlwaysOnPackage()
+ *
+ * @return result of Vpn#setAndSaveAlwaysOnPackage(String, boolean)
+ */
+ private synchronized boolean setAndSaveAlwaysOnPackage(String packageName, boolean lockdown) {
+ if (setAlwaysOnPackage(packageName, lockdown)) {
+ saveAlwaysOnPackage();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * @return {@code true} if the service was started, the service was already connected, or there
+ * was no always-on VPN to start. {@code false} otherwise.
+ */
+ public boolean startAlwaysOnVpn() {
+ final String alwaysOnPackage;
+ synchronized (this) {
+ alwaysOnPackage = getAlwaysOnPackage();
+ // Skip if there is no service to start.
+ if (alwaysOnPackage == null) {
+ return true;
+ }
+ // Skip if the service is already established. This isn't bulletproof: it's not bound
+ // until after establish(), so if it's mid-setup onStartCommand will be sent twice,
+ // which may restart the connection.
+ if (getNetworkInfo().isConnected()) {
+ return true;
+ }
+ }
+
+ // Start the VPN service declared in the app's manifest.
+ Intent serviceIntent = new Intent(VpnConfig.SERVICE_INTERFACE);
+ serviceIntent.setPackage(alwaysOnPackage);
+ try {
+ return mContext.startServiceAsUser(serviceIntent, UserHandle.of(mUserHandle)) != null;
+ } catch (RuntimeException e) {
+ Log.e(TAG, "VpnService " + serviceIntent + " failed to start", e);
+ return false;
+ }
+ }
+
+ /**
* Prepare for a VPN application. This method is designed to solve
* race conditions. It first compares the current prepared package
* with {@code oldPackage}. If they are the same, the prepared
@@ -270,11 +403,12 @@ public class Vpn {
*
* - oldPackage non-null, newPackage null: App calling VpnService#prepare().
* - oldPackage null, newPackage non-null: ConfirmDialog calling prepareVpn().
- * - oldPackage non-null, newPackage=LEGACY_VPN: Used internally to disconnect
+ * - oldPackage null, newPackage=LEGACY_VPN: Used internally to disconnect
* and revoke any current app VPN and re-prepare legacy vpn.
*
- * TODO: Rename the variables - or split this method into two - and end this
- * confusion.
+ * TODO: Rename the variables - or split this method into two - and end this confusion.
+ * TODO: b/29032008 Migrate code from prepare(oldPackage=non-null, newPackage=LEGACY_VPN)
+ * to prepare(oldPackage=null, newPackage=LEGACY_VPN)
*
* @param oldPackage The package name of the old VPN application
* @param newPackage The package name of the new VPN application
@@ -284,10 +418,7 @@ public class Vpn {
public synchronized boolean prepare(String oldPackage, String newPackage) {
if (oldPackage != null) {
// Stop an existing always-on VPN from being dethroned by other apps.
- // TODO: Replace TextUtils.equals by isCurrentPreparedPackage when ConnectivityService
- // can unset always-on after always-on package is uninstalled. Make sure when package
- // is reinstalled, the consent dialog is not shown.
- if (mAlwaysOn && !TextUtils.equals(mPackage, oldPackage)) {
+ if (mAlwaysOn && !isCurrentPreparedPackage(oldPackage)) {
return false;
}
@@ -318,9 +449,7 @@ public class Vpn {
enforceControlPermission();
// Stop an existing always-on VPN from being dethroned by other apps.
- // TODO: Replace TextUtils.equals by isCurrentPreparedPackage when ConnectivityService
- // can unset always-on after always-on package is uninstalled
- if (mAlwaysOn && !TextUtils.equals(mPackage, newPackage)) {
+ if (mAlwaysOn && !isCurrentPreparedPackage(newPackage)) {
return false;
}
@@ -862,6 +991,7 @@ public class Vpn {
setVpnForcedLocked(false);
mAlwaysOn = false;
+ unregisterPackageChangeReceiverLocked();
// Quit any active connections
agentDisconnect();
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 05f5b8f1ac6c..07dc404e08db 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1448,6 +1448,9 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" -s: short summary");
pw.println(" -d: only list dangerous permissions");
pw.println(" -u: list only the permissions users will see");
+ pw.println(" dump-profiles TARGET-PACKAGE");
+ pw.println(" Dumps method/class profile files to");
+ pw.println(" /data/misc/profman/TARGET-PACKAGE.txt");
pw.println(" resolve-activity [--brief] [--components] [--user USER_ID] INTENT");
pw.println(" Prints the activity that resolves to the given Intent.");
pw.println(" query-activities [--brief] [--components] [--user USER_ID] INTENT");
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 66bb24d02de4..698774441cca 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2589,15 +2589,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
final PhoneWindow win = new PhoneWindow(context);
win.setIsStartingWindow(true);
- final WindowManager.LayoutParams params = win.getAttributes();
final Resources r = context.getResources();
- CharSequence label = r.getText(labelRes);
- // Only change the accessibility title if the label is localized
- if (label != null) {
- win.setTitle(label, true);
- } else {
- win.setTitle(nonLocalizedLabel, false);
- }
+ win.setTitle(r.getText(labelRes, nonLocalizedLabel));
win.setType(
WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
@@ -2631,6 +2624,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
win.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT);
+ final WindowManager.LayoutParams params = win.getAttributes();
params.token = appToken;
params.packageName = packageName;
params.windowAnimations = win.getWindowStyle().getResourceId(
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 8be5dfb3c7fd..101f56f49241 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1203,6 +1203,9 @@ final class AccessibilityController {
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;