diff options
41 files changed, 1227 insertions, 342 deletions
diff --git a/api/current.txt b/api/current.txt index cf19d542117e..fd546f1cfb63 100644 --- a/api/current.txt +++ b/api/current.txt @@ -91,6 +91,7 @@ package android { field public static final java.lang.String NFC = "android.permission.NFC"; field public static final deprecated java.lang.String PERSISTENT_ACTIVITY = "android.permission.PERSISTENT_ACTIVITY"; field public static final java.lang.String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS"; + field public static final java.lang.String READ_ALL_VOICEMAIL = "com.android.voicemail.permission.READ_ALL_VOICEMAIL"; field public static final java.lang.String READ_CALENDAR = "android.permission.READ_CALENDAR"; field public static final java.lang.String READ_CALL_LOG = "android.permission.READ_CALL_LOG"; field public static final java.lang.String READ_CONTACTS = "android.permission.READ_CONTACTS"; @@ -5243,10 +5244,7 @@ package android.app.admin { method public int getPasswordQuality(android.content.ComponentName); method public boolean getStorageEncryption(android.content.ComponentName); method public int getStorageEncryptionStatus(); - method public boolean hasAnyCaCertsInstalled(); - method public boolean hasCaCertInstalled(byte[]); method public boolean hasGrantedPolicy(android.content.ComponentName, int); - method public boolean installCaCert(android.content.ComponentName, byte[]); method public boolean isActivePasswordSufficient(); method public boolean isAdminActive(android.content.ComponentName); method public boolean isApplicationBlocked(android.content.ComponentName, java.lang.String); @@ -5284,7 +5282,6 @@ package android.app.admin { method public void setRestrictionsProvider(android.content.ComponentName, android.content.ComponentName); method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String); method public int setStorageEncryption(android.content.ComponentName, boolean); - method public void uninstallCaCert(android.content.ComponentName, byte[]); method public void wipeData(int); field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN"; field public static final java.lang.String ACTION_PROVISION_MANAGED_PROFILE = "android.app.action.ACTION_PROVISION_MANAGED_PROFILE"; @@ -8164,6 +8161,8 @@ package android.content.pm { field public int requiresSmallestWidthDp; field public java.lang.String[] sharedLibraryFiles; field public java.lang.String sourceDir; + field public java.lang.String[] splitPublicSourceDirs; + field public java.lang.String[] splitSourceDirs; field public int targetSdkVersion; field public java.lang.String taskAffinity; field public int theme; @@ -8232,6 +8231,8 @@ package android.content.pm { field public boolean handleProfiling; field public java.lang.String publicSourceDir; field public java.lang.String sourceDir; + field public java.lang.String[] splitPublicSourceDirs; + field public java.lang.String[] splitSourceDirs; field public java.lang.String targetPackage; } @@ -27740,8 +27741,17 @@ package android.telecomm { } public class Subscription implements android.os.Parcelable { - ctor public Subscription(); + ctor public Subscription(android.content.ComponentName, java.lang.String, android.net.Uri, int, int, int, boolean, boolean); method public int describeContents(); + method public android.content.ComponentName getComponentName(); + method public android.net.Uri getHandle(); + method public android.graphics.drawable.Drawable getIcon(android.content.Context); + method public android.graphics.drawable.Drawable getIcon(android.content.Context, int); + method public java.lang.String getId(); + method public java.lang.String getLabel(android.content.Context); + method public java.lang.String getShortDescription(android.content.Context); + method public boolean isEnabled(); + method public boolean isSystemDefault(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; } @@ -28205,6 +28215,7 @@ package android.telephony { method public java.lang.String getSimSerialNumber(); method public int getSimState(); method public java.lang.String getSubscriberId(); + method public java.util.List<android.telecomm.Subscription> getSubscriptions(); method public java.lang.String getVoiceMailAlphaTag(); method public java.lang.String getVoiceMailNumber(); method public boolean hasIccCard(); @@ -28229,6 +28240,7 @@ package android.telephony { field public static final java.lang.String EXTRA_STATE_IDLE; field public static final java.lang.String EXTRA_STATE_OFFHOOK; field public static final java.lang.String EXTRA_STATE_RINGING; + field public static final java.lang.String EXTRA_SUBSCRIPTION = "subscription"; field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7 field public static final int NETWORK_TYPE_CDMA = 4; // 0x4 field public static final int NETWORK_TYPE_EDGE = 2; // 0x2 diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index 47047b86a6ec..f85a7dc89876 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -58,11 +58,13 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.WeakHashMap; + import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import com.android.internal.content.PackageHelper; +import com.android.internal.util.ArrayUtils; public final class Pm { IPackageManager mPm; @@ -1548,6 +1550,12 @@ public final class Pm { if (info != null && info.applicationInfo != null) { System.out.print("package:"); System.out.println(info.applicationInfo.sourceDir); + if (!ArrayUtils.isEmpty(info.applicationInfo.splitSourceDirs)) { + for (String splitSourceDir : info.applicationInfo.splitSourceDirs) { + System.out.print("package:"); + System.out.println(splitSourceDir); + } + } } } catch (RemoteException e) { System.err.println(e.toString()); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index ea4604466a6b..b8f20891164e 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -191,11 +191,13 @@ public final class ActivityThread { /** Reference to singleton {@link ActivityThread} */ private static ActivityThread sCurrentActivityThread; Instrumentation mInstrumentation; + String mInstrumentationPackageName = null; String mInstrumentationAppDir = null; - String mInstrumentationAppLibraryDir = null; - String mInstrumentationAppPackage = null; + String[] mInstrumentationSplitAppDirs = null; + String mInstrumentationLibDir = null; String mInstrumentedAppDir = null; - String mInstrumentedAppLibraryDir = null; + String[] mInstrumentedSplitAppDirs = null; + String mInstrumentedLibDir = null; boolean mSystemThread = false; boolean mJitEnabled = false; @@ -1585,11 +1587,11 @@ public final class ActivityThread { /** * Creates the top level resources for the given package. */ - Resources getTopLevelResources(String resDir, String[] overlayDirs, String[] libDirs, - int displayId, Configuration overrideConfiguration, + Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs, + String[] libDirs, int displayId, Configuration overrideConfiguration, LoadedApk pkgInfo) { - return mResourcesManager.getTopLevelResources(resDir, overlayDirs, libDirs, displayId, - overrideConfiguration, pkgInfo.getCompatibilityInfo(), null); + return mResourcesManager.getTopLevelResources(resDir, splitResDirs, overlayDirs, libDirs, + displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo(), null); } final Handler getHandler() { @@ -4315,16 +4317,20 @@ public final class ActivityThread { + data.instrumentationName); } + mInstrumentationPackageName = ii.packageName; mInstrumentationAppDir = ii.sourceDir; - mInstrumentationAppLibraryDir = ii.nativeLibraryDir; - mInstrumentationAppPackage = ii.packageName; + mInstrumentationSplitAppDirs = ii.splitSourceDirs; + mInstrumentationLibDir = ii.nativeLibraryDir; mInstrumentedAppDir = data.info.getAppDir(); - mInstrumentedAppLibraryDir = data.info.getLibDir(); + mInstrumentedSplitAppDirs = data.info.getSplitAppDirs(); + mInstrumentedLibDir = data.info.getLibDir(); ApplicationInfo instrApp = new ApplicationInfo(); instrApp.packageName = ii.packageName; instrApp.sourceDir = ii.sourceDir; instrApp.publicSourceDir = ii.publicSourceDir; + instrApp.splitSourceDirs = ii.splitSourceDirs; + instrApp.splitPublicSourceDirs = ii.splitPublicSourceDirs; instrApp.dataDir = ii.dataDir; instrApp.nativeLibraryDir = ii.nativeLibraryDir; LoadedApk pi = getPackageInfo(instrApp, data.compatInfo, diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 84673d9e238e..de0396e478d7 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -823,8 +823,10 @@ final class ApplicationPackageManager extends PackageManager { if (app.packageName.equals("system")) { return mContext.mMainThread.getSystemContext().getResources(); } + final boolean sameUid = (app.uid == Process.myUid()); Resources r = mContext.mMainThread.getTopLevelResources( - app.uid == Process.myUid() ? app.sourceDir : app.publicSourceDir, + sameUid ? app.sourceDir : app.publicSourceDir, + sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs, app.resourceDirs, null, Display.DEFAULT_DISPLAY, null, mContext.mPackageInfo); if (r != null) { return r; diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index a42bd3b92730..3e7d9b450708 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2190,10 +2190,10 @@ class ContextImpl extends Context { || overrideConfiguration != null || (compatInfo != null && compatInfo.applicationScale != resources.getCompatibilityInfo().applicationScale)) { - resources = mResourcesManager.getTopLevelResources( - packageInfo.getResDir(), packageInfo.getOverlayDirs(), - packageInfo.getApplicationInfo().sharedLibraryFiles, - displayId, overrideConfiguration, compatInfo, activityToken); + resources = mResourcesManager.getTopLevelResources(packageInfo.getResDir(), + packageInfo.getSplitResDirs(), packageInfo.getOverlayDirs(), + packageInfo.getApplicationInfo().sharedLibraryFiles, displayId, + overrideConfiguration, compatInfo, activityToken); } } mResources = resources; diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 3ae8bfcb4f3f..065e88db320d 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -16,8 +16,8 @@ package android.app; +import android.text.TextUtils; import android.util.ArrayMap; -import com.android.internal.util.ArrayUtils; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -52,6 +52,8 @@ import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; import java.util.Enumeration; final class IntentReceiverLeaked extends AndroidRuntimeException { @@ -79,6 +81,8 @@ public final class LoadedApk { final String mPackageName; private final String mAppDir; private final String mResDir; + private final String[] mSplitAppDirs; + private final String[] mSplitResDirs; private final String[] mOverlayDirs; private final String[] mSharedLibraries; private final String mDataDir; @@ -116,13 +120,14 @@ public final class LoadedApk { public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo, CompatibilityInfo compatInfo, ClassLoader baseLoader, boolean securityViolation, boolean includeCode) { + final int myUid = Process.myUid(); mActivityThread = activityThread; mApplicationInfo = aInfo; mPackageName = aInfo.packageName; mAppDir = aInfo.sourceDir; - final int myUid = Process.myUid(); - mResDir = aInfo.uid == myUid ? aInfo.sourceDir - : aInfo.publicSourceDir; + mResDir = aInfo.uid == myUid ? aInfo.sourceDir : aInfo.publicSourceDir; + mSplitAppDirs = aInfo.splitSourceDirs; + mSplitResDirs = aInfo.uid == myUid ? aInfo.splitSourceDirs : aInfo.splitPublicSourceDirs; mOverlayDirs = aInfo.resourceDirs; if (!UserHandle.isSameUser(aInfo.uid, myUid) && !Process.isIsolated()) { aInfo.dataDir = PackageManager.getDataDirForUser(UserHandle.getUserId(myUid), @@ -149,6 +154,8 @@ public final class LoadedApk { mPackageName = "android"; mAppDir = null; mResDir = null; + mSplitAppDirs = null; + mSplitResDirs = null; mOverlayDirs = null; mSharedLibraries = null; mDataDir = null; @@ -214,53 +221,6 @@ public final class LoadedApk { return ai.sharedLibraryFiles; } - /** - * Combines two arrays (of library names) such that they are - * concatenated in order but are devoid of duplicates. The - * result is a single string with the names of the libraries - * separated by colons, or <code>null</code> if both lists - * were <code>null</code> or empty. - * - * @param list1 null-ok; the first list - * @param list2 null-ok; the second list - * @return null-ok; the combination - */ - private static String combineLibs(String[] list1, String[] list2) { - StringBuilder result = new StringBuilder(300); - boolean first = true; - - if (list1 != null) { - for (String s : list1) { - if (first) { - first = false; - } else { - result.append(':'); - } - result.append(s); - } - } - - // Only need to check for duplicates if list1 was non-empty. - boolean dupCheck = !first; - - if (list2 != null) { - for (String s : list2) { - if (dupCheck && ArrayUtils.contains(list1, s)) { - continue; - } - - if (first) { - first = false; - } else { - result.append(':'); - } - result.append(s); - } - } - - return result.toString(); - } - public ClassLoader getClassLoader() { synchronized (this) { if (mClassLoader != null) { @@ -268,8 +228,15 @@ public final class LoadedApk { } if (mIncludeCode && !mPackageName.equals("android")) { - String zip = mAppDir; - String libraryPath = mLibDir; + final ArrayList<String> zipPaths = new ArrayList<>(); + final ArrayList<String> libPaths = new ArrayList<>(); + + zipPaths.add(mAppDir); + if (mSplitAppDirs != null) { + Collections.addAll(zipPaths, mSplitAppDirs); + } + + libPaths.add(mLibDir); /* * The following is a bit of a hack to inject @@ -280,50 +247,70 @@ public final class LoadedApk { * concatenation of both apps' shared library lists. */ - String instrumentationAppDir = - mActivityThread.mInstrumentationAppDir; - String instrumentationAppLibraryDir = - mActivityThread.mInstrumentationAppLibraryDir; - String instrumentationAppPackage = - mActivityThread.mInstrumentationAppPackage; - String instrumentedAppDir = - mActivityThread.mInstrumentedAppDir; - String instrumentedAppLibraryDir = - mActivityThread.mInstrumentedAppLibraryDir; + String instrumentationPackageName = mActivityThread.mInstrumentationPackageName; + String instrumentationAppDir = mActivityThread.mInstrumentationAppDir; + String[] instrumentationSplitAppDirs = mActivityThread.mInstrumentationSplitAppDirs; + String instrumentationLibDir = mActivityThread.mInstrumentationLibDir; + + String instrumentedAppDir = mActivityThread.mInstrumentedAppDir; + String[] instrumentedSplitAppDirs = mActivityThread.mInstrumentedSplitAppDirs; + String instrumentedLibDir = mActivityThread.mInstrumentedLibDir; String[] instrumentationLibs = null; if (mAppDir.equals(instrumentationAppDir) || mAppDir.equals(instrumentedAppDir)) { - zip = instrumentationAppDir + ":" + instrumentedAppDir; - libraryPath = instrumentationAppLibraryDir + ":" + instrumentedAppLibraryDir; - if (! instrumentedAppDir.equals(instrumentationAppDir)) { - instrumentationLibs = - getLibrariesFor(instrumentationAppPackage); + zipPaths.clear(); + zipPaths.add(instrumentationAppDir); + if (instrumentationSplitAppDirs != null) { + Collections.addAll(zipPaths, instrumentationSplitAppDirs); + } + zipPaths.add(instrumentedAppDir); + if (instrumentedSplitAppDirs != null) { + Collections.addAll(zipPaths, instrumentedSplitAppDirs); + } + + libPaths.clear(); + libPaths.add(instrumentationLibDir); + libPaths.add(instrumentedLibDir); + + if (!instrumentedAppDir.equals(instrumentationAppDir)) { + instrumentationLibs = getLibrariesFor(instrumentationPackageName); } } - if ((mSharedLibraries != null) || - (instrumentationLibs != null)) { - zip = - combineLibs(mSharedLibraries, instrumentationLibs) - + ':' + zip; + if (mSharedLibraries != null) { + for (String lib : mSharedLibraries) { + if (!zipPaths.contains(lib)) { + zipPaths.add(0, lib); + } + } } + if (instrumentationLibs != null) { + for (String lib : instrumentationLibs) { + if (!zipPaths.contains(lib)) { + zipPaths.add(0, lib); + } + } + } + + final String zip = TextUtils.join(File.pathSeparator, zipPaths); + final String lib = TextUtils.join(File.pathSeparator, libPaths); + /* * With all the combination done (if necessary, actually * create the class loader. */ if (ActivityThread.localLOGV) - Slog.v(ActivityThread.TAG, "Class path: " + zip + ", JNI path: " + libraryPath); + Slog.v(ActivityThread.TAG, "Class path: " + zip + ", JNI path: " + lib); // Temporarily disable logging of disk reads on the Looper thread // as this is early and necessary. StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); - mClassLoader = - ApplicationLoaders.getDefault().getClassLoader( - zip, libraryPath, mBaseClassLoader); + mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, lib, + mBaseClassLoader); initializeJavaContextClassLoader(); StrictMode.setThreadPolicy(oldPolicy); @@ -469,6 +456,14 @@ public final class LoadedApk { return mResDir; } + public String[] getSplitAppDirs() { + return mSplitAppDirs; + } + + public String[] getSplitResDirs() { + return mSplitResDirs; + } + public String[] getOverlayDirs() { return mOverlayDirs; } @@ -487,7 +482,7 @@ public final class LoadedApk { public Resources getResources(ActivityThread mainThread) { if (mResources == null) { - mResources = mainThread.getTopLevelResources(mResDir, mOverlayDirs, + mResources = mainThread.getTopLevelResources(mResDir, mSplitResDirs, mOverlayDirs, mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, null, this); } return mResources; diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index a67faa09be86..3c1311537848 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -149,9 +149,9 @@ public class ResourcesManager { * @param compatInfo the compability info. Must not be null. * @param token the application token for determining stack bounds. */ - public Resources getTopLevelResources(String resDir, String[] overlayDirs, String[] libDirs, - int displayId, Configuration overrideConfiguration, CompatibilityInfo compatInfo, - IBinder token) { + public Resources getTopLevelResources(String resDir, String[] splitResDirs, + String[] overlayDirs, String[] libDirs, int displayId, + Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) { final float scale = compatInfo.applicationScale; ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale, token); Resources r; @@ -182,6 +182,14 @@ public class ResourcesManager { return null; } + if (splitResDirs != null) { + for (String splitResDir : splitResDirs) { + if (assets.addAssetPath(splitResDir) == 0) { + return null; + } + } + } + if (overlayDirs != null) { for (String idmapPath : overlayDirs) { assets.addOverlayPath(idmapPath); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 99f68d08e7b3..e80c7616204f 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1506,11 +1506,12 @@ public class DevicePolicyManager { * * @return false if the certBuffer cannot be parsed or installation is * interrupted, otherwise true + * @hide */ - public boolean installCaCert(ComponentName who, byte[] certBuffer) { + public boolean installCaCert(byte[] certBuffer) { if (mService != null) { try { - return mService.installCaCert(who, certBuffer); + return mService.installCaCert(certBuffer); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -1520,14 +1521,13 @@ public class DevicePolicyManager { /** * Uninstalls the given certificate from the list of User CAs, if present. + * + * @hide */ - public void uninstallCaCert(ComponentName who, byte[] certBuffer) { + public void uninstallCaCert(byte[] certBuffer) { if (mService != null) { try { - final String alias = getCaCertAlias(certBuffer); - mService.uninstallCaCert(who, alias); - } catch (CertificateException e) { - Log.w(TAG, "Unable to parse certificate", e); + mService.uninstallCaCert(certBuffer); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -1536,8 +1536,10 @@ public class DevicePolicyManager { /** * Returns whether there are any user-installed CA certificates. + * + * @hide */ - public boolean hasAnyCaCertsInstalled() { + public static boolean hasAnyCaCertsInstalled() { TrustedCertificateStore certStore = new TrustedCertificateStore(); Set<String> aliases = certStore.userAliases(); return aliases != null && !aliases.isEmpty(); @@ -1545,10 +1547,18 @@ public class DevicePolicyManager { /** * Returns whether this certificate has been installed as a User CA. + * + * @hide */ public boolean hasCaCertInstalled(byte[] certBuffer) { + TrustedCertificateStore certStore = new TrustedCertificateStore(); + String alias; + byte[] pemCert; try { - return getCaCertAlias(certBuffer) != null; + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + X509Certificate cert = (X509Certificate) certFactory.generateCertificate( + new ByteArrayInputStream(certBuffer)); + return certStore.getCertificateAlias(cert) != null; } catch (CertificateException ce) { Log.w(TAG, "Could not parse certificate", ce); } @@ -1556,17 +1566,6 @@ public class DevicePolicyManager { } /** - * Returns the alias of a given CA certificate in the certificate store, or null if it - * doesn't exist. - */ - private static String getCaCertAlias(byte[] certBuffer) throws CertificateException { - final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); - final X509Certificate cert = (X509Certificate) certFactory.generateCertificate( - new ByteArrayInputStream(certBuffer)); - return new TrustedCertificateStore().getCertificateAlias(cert); - } - - /** * Called by an application that is administering the device to disable all cameras * on the device. After setting this, no applications will be able to access any cameras * on the device. diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index e935da74b9bc..a1caa2199b1d 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -115,8 +115,8 @@ interface IDevicePolicyManager { String getProfileOwnerName(int userHandle); void setProfileEnabled(in ComponentName who); - boolean installCaCert(in ComponentName admin, in byte[] certBuffer); - void uninstallCaCert(in ComponentName admin, in String alias); + boolean installCaCert(in byte[] certBuffer); + void uninstallCaCert(in byte[] certBuffer); void addPersistentPreferredActivity(in ComponentName admin, in IntentFilter filter, in ComponentName activity); void clearPackagePersistentPreferredActivities(in ComponentName admin, String packageName); diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 6b44a115cd42..06f4019df06d 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -23,8 +23,12 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.Printer; +import com.android.internal.util.ArrayUtils; + import java.text.Collator; +import java.util.Arrays; import java.util.Comparator; +import java.util.Objects; /** * Information you can retrieve about a particular application. This @@ -398,17 +402,30 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public int largestWidthLimitDp = 0; /** - * Full path to the location of this package. + * Full path to the base APK for this application. */ public String sourceDir; /** - * Full path to the location of the publicly available parts of this - * package (i.e. the primary resource package and manifest). For - * non-forward-locked apps this will be the same as {@link #sourceDir). + * Full path to the publicly available parts of {@link #sourceDir}, + * including resources and manifest. This may be different from + * {@link #sourceDir} if an application is forward locked. */ public String publicSourceDir; - + + /** + * Full paths to zero or more split APKs that, when combined with the base + * APK defined in {@link #sourceDir}, form a complete application. + */ + public String[] splitSourceDirs; + + /** + * Full path to the publicly available parts of {@link #splitSourceDirs}, + * including resources and manifest. This may be different from + * {@link #splitSourceDirs} if an application is forward locked. + */ + public String[] splitPublicSourceDirs; + /** * Full paths to the locations of extra resource packages this application * uses. This field is only used if there are extra resource packages, @@ -512,13 +529,16 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { + " compatibleWidthLimitDp=" + compatibleWidthLimitDp + " largestWidthLimitDp=" + largestWidthLimitDp); pw.println(prefix + "sourceDir=" + sourceDir); - if (sourceDir == null) { - if (publicSourceDir != null) { - pw.println(prefix + "publicSourceDir=" + publicSourceDir); - } - } else if (!sourceDir.equals(publicSourceDir)) { + if (!Objects.equals(sourceDir, publicSourceDir)) { pw.println(prefix + "publicSourceDir=" + publicSourceDir); } + if (!ArrayUtils.isEmpty(splitSourceDirs)) { + pw.println(prefix + "splitSourceDirs=" + Arrays.toString(splitSourceDirs)); + } + if (!ArrayUtils.isEmpty(splitPublicSourceDirs) + && !Arrays.equals(splitSourceDirs, splitPublicSourceDirs)) { + pw.println(prefix + "splitPublicSourceDirs=" + Arrays.toString(splitPublicSourceDirs)); + } if (resourceDirs != null) { pw.println(prefix + "resourceDirs=" + resourceDirs); } @@ -591,6 +611,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { largestWidthLimitDp = orig.largestWidthLimitDp; sourceDir = orig.sourceDir; publicSourceDir = orig.publicSourceDir; + splitSourceDirs = orig.splitSourceDirs; + splitPublicSourceDirs = orig.splitPublicSourceDirs; nativeLibraryDir = orig.nativeLibraryDir; cpuAbi = orig.cpuAbi; resourceDirs = orig.resourceDirs; @@ -633,6 +655,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeInt(largestWidthLimitDp); dest.writeString(sourceDir); dest.writeString(publicSourceDir); + dest.writeStringArray(splitSourceDirs); + dest.writeStringArray(splitPublicSourceDirs); dest.writeString(nativeLibraryDir); dest.writeString(cpuAbi); dest.writeStringArray(resourceDirs); @@ -674,6 +698,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { largestWidthLimitDp = source.readInt(); sourceDir = source.readString(); publicSourceDir = source.readString(); + splitSourceDirs = source.readStringArray(); + splitPublicSourceDirs = source.readStringArray(); nativeLibraryDir = source.readString(); cpuAbi = source.readString(); resourceDirs = source.readStringArray(); diff --git a/core/java/android/content/pm/InstrumentationInfo.java b/core/java/android/content/pm/InstrumentationInfo.java index a977e41c3d69..dab0caffd57a 100644 --- a/core/java/android/content/pm/InstrumentationInfo.java +++ b/core/java/android/content/pm/InstrumentationInfo.java @@ -30,17 +30,32 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable { * "package" attribute. */ public String targetPackage; - + /** - * Full path to the location of this package. + * Full path to the base APK for this application. */ public String sourceDir; - + /** - * Full path to the location of the publicly available parts of this package (i.e. the resources - * and manifest). For non-forward-locked apps this will be the same as {@link #sourceDir). + * Full path to the publicly available parts of {@link #sourceDir}, + * including resources and manifest. This may be different from + * {@link #sourceDir} if an application is forward locked. */ public String publicSourceDir; + + /** + * Full paths to zero or more split APKs that, when combined with the base + * APK defined in {@link #sourceDir}, form a complete application. + */ + public String[] splitSourceDirs; + + /** + * Full path to the publicly available parts of {@link #splitSourceDirs}, + * including resources and manifest. This may be different from + * {@link #splitSourceDirs} if an application is forward locked. + */ + public String[] splitPublicSourceDirs; + /** * Full path to a directory assigned to the package for its persistent * data. diff --git a/core/java/android/hardware/ICameraService.aidl b/core/java/android/hardware/ICameraService.aidl index 4c50dda1f096..31896f55b09f 100644 --- a/core/java/android/hardware/ICameraService.aidl +++ b/core/java/android/hardware/ICameraService.aidl @@ -69,4 +69,9 @@ interface ICameraService * well-formatted in the generated java method. */ int getCameraVendorTagDescriptor(out BinderHolder desc); + + // Writes the camera1 parameters into a single-element array. + int getLegacyParameters(int cameraId, out String[] parameters); + // Determines if a particular API version is supported; see ICameraService.h for version defines + int supportsCameraApi(int cameraId, int apiVersion); } diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 09015622d2a0..73188ff0898b 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -19,8 +19,10 @@ package android.hardware.camera2; import android.content.Context; import android.hardware.ICameraService; import android.hardware.ICameraServiceListener; +import android.hardware.CameraInfo; import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.legacy.CameraDeviceUserShim; +import android.hardware.camera2.legacy.LegacyMetadataMapper; import android.hardware.camera2.utils.CameraBinderDecorator; import android.hardware.camera2.utils.CameraRuntimeException; import android.hardware.camera2.utils.BinderHolder; @@ -57,6 +59,10 @@ public final class CameraManager { private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera"; private static final int USE_CALLING_UID = -1; + @SuppressWarnings("unused") + private static final int API_VERSION_1 = 1; + private static final int API_VERSION_2 = 2; + private final ICameraService mCameraService; private ArrayList<String> mDeviceIdList; @@ -142,6 +148,9 @@ public final class CameraManager { synchronized (mLock) { mListenerMap.put(listener, handler); + + // TODO: fire the current oldest known state when adding a new listener + // (must be done while holding lock) } } @@ -185,16 +194,46 @@ public final class CameraManager { } } - CameraMetadataNative info = new CameraMetadataNative(); - try { - mCameraService.getCameraCharacteristics(Integer.valueOf(cameraId), info); - } catch(CameraRuntimeException e) { - throw e.asChecked(); - } catch(RemoteException e) { - // impossible - return null; + int id = Integer.valueOf(cameraId); + + /* + * Get the camera characteristics from the camera service directly if it supports it, + * otherwise get them from the legacy shim instead. + */ + + if (!supportsCamera2Api(cameraId)) { + // Legacy backwards compatibility path; build static info from the camera parameters + String[] outParameters = new String[1]; + try { + mCameraService.getLegacyParameters(id, /*out*/outParameters); + String parameters = outParameters[0]; + + CameraInfo info = new CameraInfo(); + mCameraService.getCameraInfo(id, /*out*/info); + + return LegacyMetadataMapper.createCharacteristics(parameters, info); + } catch (RemoteException e) { + // Impossible + return null; + } catch (CameraRuntimeException e) { + throw e.asChecked(); + } + + } else { + // Normal path: Get the camera characteristics directly from the camera service + CameraMetadataNative info = new CameraMetadataNative(); + + try { + mCameraService.getCameraCharacteristics(id, info); + } catch(CameraRuntimeException e) { + throw e.asChecked(); + } catch(RemoteException e) { + // impossible + return null; + } + + return new CameraCharacteristics(info); } - return new CameraCharacteristics(info); } /** @@ -456,6 +495,53 @@ public final class CameraManager { } } + /** + * Queries the camera service if it supports the camera2 api directly, or needs a shim. + * + * @param cameraId a non-{@code null} camera identifier + * @return {@code false} if the legacy shim needs to be used, {@code true} otherwise. + */ + private boolean supportsCamera2Api(String cameraId) { + return supportsCameraApi(cameraId, API_VERSION_2); + } + + /** + * Queries the camera service if it supports a camera api directly, or needs a shim. + * + * @param cameraId a non-{@code null} camera identifier + * @param apiVersion the version, i.e. {@code API_VERSION_1} or {@code API_VERSION_2} + * @return {@code true} if connecting will work for that device version. + */ + private boolean supportsCameraApi(String cameraId, int apiVersion) { + int id = Integer.parseInt(cameraId); + + /* + * Possible return values: + * - NO_ERROR => Camera2 API is supported + * - CAMERA_DEPRECATED_HAL => Camera2 API is *not* supported (thrown as an exception) + * + * Anything else is an unexpected error we don't want to recover from. + */ + + try { + int res = mCameraService.supportsCameraApi(id, apiVersion); + + if (res != CameraBinderDecorator.NO_ERROR) { + throw new AssertionError("Unexpected value " + res); + } + + return true; + } catch (CameraRuntimeException e) { + if (e.getReason() == CameraAccessException.CAMERA_DEPRECATED_HAL) { + return false; + } else { + throw e; + } + } catch (RemoteException e) { + throw new AssertionError("Camera service unreachable", e); + } + } + // TODO: this class needs unit tests // TODO: extract class into top level private class CameraServiceListener extends ICameraServiceListener.Stub { diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java index 22ff9c63114f..ab7e8449230c 100644 --- a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java +++ b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java @@ -34,6 +34,7 @@ import android.util.Log; * <li>{@code CONFIGURING -> IDLE}</li> * <li>{@code IDLE -> CONFIGURING}</li> * <li>{@code IDLE -> CAPTURING}</li> + * <li>{@code IDLE -> IDLE}</li> * <li>{@code CAPTURING -> IDLE}</li> * <li>{@code ANY -> ERROR}</li> * </ul> @@ -216,12 +217,17 @@ public class CameraDeviceState { mCurrentState = STATE_CONFIGURING; break; case STATE_IDLE: + if (mCurrentState == STATE_IDLE) { + break; + } + if (mCurrentState != STATE_CONFIGURING && mCurrentState != STATE_CAPTURING) { Log.e(TAG, "Cannot call idle while in state: " + mCurrentState); mCurrentError = CameraBinderDecorator.INVALID_OPERATION; doStateTransition(STATE_ERROR); break; } + if (mCurrentState != STATE_IDLE && mCurrentHandler != null && mCurrentListener != null) { mCurrentHandler.post(new Runnable() { diff --git a/core/java/android/hardware/camera2/legacy/GLThreadManager.java b/core/java/android/hardware/camera2/legacy/GLThreadManager.java index 3fd2309912ec..038255762040 100644 --- a/core/java/android/hardware/camera2/legacy/GLThreadManager.java +++ b/core/java/android/hardware/camera2/legacy/GLThreadManager.java @@ -148,6 +148,12 @@ public class GLThreadManager { Handler handler = mGLHandlerThread.getHandler(); handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP)); mGLHandlerThread.quitSafely(); + try { + mGLHandlerThread.join(); + } catch (InterruptedException e) { + Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.", + mGLHandlerThread.getName(), mGLHandlerThread.getId())); + } } /** diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java index f9cf90596973..c34a34de021e 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java +++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java @@ -48,7 +48,6 @@ import java.util.concurrent.atomic.AtomicInteger; */ public class LegacyCameraDevice implements AutoCloseable { public static final String DEBUG_PROP = "HAL1ShimLogging"; - private final String TAG; private final int mCameraId; @@ -56,10 +55,11 @@ public class LegacyCameraDevice implements AutoCloseable { private final CameraDeviceState mDeviceState = new CameraDeviceState(); private final ConditionVariable mIdle = new ConditionVariable(/*open*/true); - private final AtomicInteger mRequestIdCounter = new AtomicInteger(0); - private final HandlerThread mCallbackHandlerThread = new HandlerThread("ResultThread"); + private final HandlerThread mResultThread = new HandlerThread("ResultThread"); + private final HandlerThread mCallbackHandlerThread = new HandlerThread("CallbackThread"); private final Handler mCallbackHandler; + private final Handler mResultHandler; private static final int ILLEGAL_VALUE = -1; private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) { @@ -81,11 +81,18 @@ public class LegacyCameraDevice implements AutoCloseable { public void onError(final int errorCode, RequestHolder holder) { mIdle.open(); final CaptureResultExtras extras = getExtrasFromRequest(holder); - try { - mDeviceCallbacks.onCameraError(errorCode, extras); - } catch (RemoteException e) { - Log.e(TAG, "Received remote exception during onCameraError callback: ", e); - } + mResultHandler.post(new Runnable() { + @Override + public void run() { + try { + mDeviceCallbacks.onCameraError(errorCode, extras); + } catch (RemoteException e) { + throw new IllegalStateException( + "Received remote exception during onCameraError callback: ", e); + } + } + }); + } @@ -98,36 +105,55 @@ public class LegacyCameraDevice implements AutoCloseable { public void onIdle() { mIdle.open(); - try { - mDeviceCallbacks.onCameraIdle(); - } catch (RemoteException e) { - Log.e(TAG, "Received remote exception during onCameraIdle callback: ", e); - } + mResultHandler.post(new Runnable() { + @Override + public void run() { + try { + mDeviceCallbacks.onCameraIdle(); + } catch (RemoteException e) { + throw new IllegalStateException( + "Received remote exception during onCameraIdle callback: ", e); + } + } + }); } @Override public void onCaptureStarted(RequestHolder holder) { final CaptureResultExtras extras = getExtrasFromRequest(holder); - try { - // TODO: Don't fake timestamp - mDeviceCallbacks.onCaptureStarted(extras, System.nanoTime()); - } catch (RemoteException e) { - Log.e(TAG, "Received remote exception during onCameraError callback: ", e); - } + final long timestamp = System.nanoTime(); + mResultHandler.post(new Runnable() { + @Override + public void run() { + try { + // TODO: Don't fake timestamp + mDeviceCallbacks.onCaptureStarted(extras, timestamp); + } catch (RemoteException e) { + throw new IllegalStateException( + "Received remote exception during onCameraError callback: ", e); + } + } + }); } @Override - public void onCaptureResult(CameraMetadataNative result, RequestHolder holder) { + public void onCaptureResult(final CameraMetadataNative result, RequestHolder holder) { final CaptureResultExtras extras = getExtrasFromRequest(holder); - try { - // TODO: Don't fake metadata - mDeviceCallbacks.onResultReceived(result, extras); - } catch (RemoteException e) { - Log.e(TAG, "Received remote exception during onCameraError callback: ", e); - } + mResultHandler.post(new Runnable() { + @Override + public void run() { + try { + // TODO: Don't fake metadata + mDeviceCallbacks.onResultReceived(result, extras); + } catch (RemoteException e) { + throw new IllegalStateException( + "Received remote exception during onCameraError callback: ", e); + } + } + }); } }; @@ -161,6 +187,8 @@ public class LegacyCameraDevice implements AutoCloseable { mDeviceCallbacks = callbacks; TAG = String.format("CameraDevice-%d-LE", mCameraId); + mResultThread.start(); + mResultHandler = new Handler(mResultThread.getLooper()); mCallbackHandlerThread.start(); mCallbackHandler = new Handler(mCallbackHandlerThread.getLooper()); mDeviceState.setCameraDeviceCallbacks(mCallbackHandler, mStateListener); @@ -244,6 +272,22 @@ public class LegacyCameraDevice implements AutoCloseable { public void close() { mRequestThreadManager.quit(); mCallbackHandlerThread.quitSafely(); + mResultThread.quitSafely(); + + try { + mCallbackHandlerThread.join(); + } catch (InterruptedException e) { + Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.", + mCallbackHandlerThread.getName(), mCallbackHandlerThread.getId())); + } + + try { + mResultThread.join(); + } catch (InterruptedException e) { + Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.", + mResultThread.getName(), mResultThread.getId())); + } + // TODO: throw IllegalStateException in every method after close has been called } diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java new file mode 100644 index 000000000000..8bb066fd09a3 --- /dev/null +++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.camera2.legacy; + +import android.graphics.ImageFormat; +import android.hardware.Camera; +import android.hardware.Camera.CameraInfo; +import android.hardware.Camera.Size; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.impl.CameraMetadataNative; +import android.hardware.camera2.params.StreamConfiguration; +import android.hardware.camera2.params.StreamConfigurationDuration; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +import static com.android.internal.util.Preconditions.*; +import static android.hardware.camera2.CameraCharacteristics.*; + +/** + * Provide legacy-specific implementations of camera2 metadata for legacy devices, such as the + * camera characteristics. + */ +public class LegacyMetadataMapper { + private static final String TAG = "LegacyMetadataMapper"; + private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); + + // from graphics.h + private static final int HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22; + private static final int HAL_PIXEL_FORMAT_BLOB = 0x21; + + /** + * Create characteristics for a legacy device by mapping the {@code parameters} + * and {@code info} + * + * @param parameters A string parseable by {@link Camera.Parameters#unflatten} + * @param info Camera info with camera facing direction and angle of orientation + * @return static camera characteristics for a camera device + * + * @throws NullPointerException if any of the args were {@code null} + */ + public static CameraCharacteristics createCharacteristics(String parameters, + android.hardware.CameraInfo info) { + checkNotNull(parameters, "parameters must not be null"); + checkNotNull(info, "info must not be null"); + checkNotNull(info.info, "info.info must not be null"); + + CameraMetadataNative m = new CameraMetadataNative(); + + mapCameraInfo(m, info.info); + + Camera.Parameters params = Camera.getEmptyParameters(); + params.unflatten(parameters); + mapCameraParameters(m, params); + + if (VERBOSE) { + Log.v(TAG, "createCharacteristics metadata:"); + Log.v(TAG, "--------------------------------------------------- (start)"); + m.dumpToLog(); + Log.v(TAG, "--------------------------------------------------- (end)"); + } + + return new CameraCharacteristics(m); + } + + private static void mapCameraInfo(CameraMetadataNative m, CameraInfo i) { + m.set(LENS_FACING, i.facing == CameraInfo.CAMERA_FACING_BACK ? + LENS_FACING_BACK : LENS_FACING_FRONT); + m.set(SENSOR_ORIENTATION, i.orientation); + } + + private static void mapCameraParameters(CameraMetadataNative m, Camera.Parameters p) { + mapStreamConfigs(m, p); + + // TODO: map other fields + } + + private static void mapStreamConfigs(CameraMetadataNative m, Camera.Parameters p) { + // TODO: set non-empty durations + m.set(SCALER_AVAILABLE_MIN_FRAME_DURATIONS, new StreamConfigurationDuration[] {} ); + m.set(SCALER_AVAILABLE_STALL_DURATIONS, new StreamConfigurationDuration[] {} ); + + ArrayList<StreamConfiguration> availableStreamConfigs = new ArrayList<>(); + /* + * Implementation-defined (preview, recording, etc) -> use camera1 preview sizes + * YUV_420_888 cpu callbacks -> use camera1 preview sizes + * Other preview callbacks (CPU) -> use camera1 preview sizes + * JPEG still capture -> use camera1 still capture sizes + * + * Use platform-internal format constants here, since StreamConfigurationMap does the + * remapping to public format constants. + */ + List<Size> previewSizes = p.getSupportedPreviewSizes(); + appendStreamConfig(availableStreamConfigs, + HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, previewSizes); + appendStreamConfig(availableStreamConfigs, + ImageFormat.YUV_420_888, previewSizes); + for (int format : p.getSupportedPreviewFormats()) { + if (ImageFormat.isPublicFormat(format)) { + appendStreamConfig(availableStreamConfigs, format, previewSizes); + } else { + /* + * Do not add any formats unknown to us + * (since it would fail runtime checks in StreamConfigurationMap) + */ + Log.w(TAG, + String.format("mapStreamConfigs - Skipping non-public format %x", format)); + } + } + appendStreamConfig(availableStreamConfigs, + HAL_PIXEL_FORMAT_BLOB, p.getSupportedPictureSizes()); + m.set(SCALER_AVAILABLE_STREAM_CONFIGURATIONS, + availableStreamConfigs.toArray(new StreamConfiguration[0])); + } + + private static void appendStreamConfig( + ArrayList<StreamConfiguration> configs, int format, List<Camera.Size> sizes) { + for (Camera.Size size : sizes) { + StreamConfiguration config = + new StreamConfiguration(format, size.width, size.height, /*input*/false); + configs.add(config); + } + } +} diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java index c4669f516b1b..7b522ffa36ce 100644 --- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java +++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java @@ -20,6 +20,7 @@ import android.graphics.ImageFormat; import android.graphics.SurfaceTexture; import android.hardware.Camera; import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; import android.hardware.camera2.utils.LongParcelable; import android.hardware.camera2.impl.CameraMetadataNative; import android.os.ConditionVariable; @@ -28,12 +29,15 @@ import android.os.Message; import android.os.SystemClock; import android.util.Log; import android.util.Pair; +import android.util.Size; import android.view.Surface; import java.io.IOError; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; import java.util.List; /** @@ -64,6 +68,7 @@ public class RequestThreadManager { private static final int PREVIEW_FRAME_TIMEOUT = 300; // ms private static final int JPEG_FRAME_TIMEOUT = 1000; // ms + private static final float ASPECT_RATIO_TOLERANCE = 0.01f; private boolean mPreviewRunning = false; private volatile RequestHolder mInFlightPreview; @@ -74,6 +79,8 @@ public class RequestThreadManager { private GLThreadManager mGLThreadManager; private SurfaceTexture mPreviewTexture; + private Size mIntermediateBufferSize; + private final RequestQueue mRequestQueue = new RequestQueue(); private SurfaceTexture mDummyTexture; private Surface mDummySurface; @@ -93,6 +100,31 @@ public class RequestThreadManager { } } + + /** + * Comparator for {@link Size} objects. + * + * <p>This comparator compares by rectangle area. Tiebreaks on width.</p> + */ + private static class SizeComparator implements Comparator<Size> { + @Override + public int compare(Size size, Size size2) { + if (size == null || size2 == null) { + throw new NullPointerException("Null argument passed to compare"); + } + if (size.equals(size2)) return 0; + long width = size.getWidth(); + long width2 = size2.getWidth(); + long area = width * size.getHeight(); + long area2 = width2 * size2.getHeight(); + if (area == area2) { + return (width > width2) ? 1 : -1; + } + return (area > area2) ? 1 : -1; + + } + } + /** * Counter class used to calculate and log the current FPS of frame production. */ @@ -230,7 +262,13 @@ public class RequestThreadManager { return; // Already running } - mPreviewTexture.setDefaultBufferSize(640, 480); // TODO: size selection based on request + if (mPreviewTexture == null) { + throw new IllegalStateException( + "Preview capture called with no preview surfaces configured."); + } + + mPreviewTexture.setDefaultBufferSize(mIntermediateBufferSize.getWidth(), + mIntermediateBufferSize.getHeight()); mCamera.setPreviewTexture(mPreviewTexture); Camera.Parameters params = mCamera.getParameters(); List<int[]> supportedFpsRanges = params.getSupportedPreviewFpsRange(); @@ -248,6 +286,7 @@ public class RequestThreadManager { startPreview(); } + private void configureOutputs(Collection<Surface> outputs) throws IOException { stopPreview(); if (mGLThreadManager != null) { @@ -261,6 +300,7 @@ public class RequestThreadManager { mInFlightPreview = null; mInFlightJpeg = null; + for (Surface s : outputs) { int format = LegacyCameraDevice.nativeDetectSurfaceType(s); switch (format) { @@ -273,6 +313,52 @@ public class RequestThreadManager { } } + if (mPreviewOutputs.size() > 0) { + List<Size> outputSizes = new ArrayList<>(outputs.size()); + for (Surface s : mPreviewOutputs) { + int[] dimens = {0, 0}; + LegacyCameraDevice.nativeDetectSurfaceDimens(s, dimens); + outputSizes.add(new Size(dimens[0], dimens[1])); + } + + Size largestOutput = findLargestByArea(outputSizes); + + Camera.Parameters params = mCamera.getParameters(); + + // Find largest jpeg dimension - assume to have the same aspect ratio as sensor. + List<Size> supportedJpegSizes = convertSizeList(params.getSupportedPictureSizes()); + Size largestJpegDimen = findLargestByArea(supportedJpegSizes); + + List<Size> supportedPreviewSizes = convertSizeList(params.getSupportedPreviewSizes()); + + // Use smallest preview dimension with same aspect ratio as sensor that is >= than all + // of the configured output dimensions. If none exists, fall back to using the largest + // supported preview size. + long largestOutputArea = largestOutput.getHeight() * (long) largestOutput.getWidth(); + Size bestPreviewDimen = findLargestByArea(supportedPreviewSizes); + for (Size s : supportedPreviewSizes) { + long currArea = s.getWidth() * s.getHeight(); + long bestArea = bestPreviewDimen.getWidth() * bestPreviewDimen.getHeight(); + if (checkAspectRatiosMatch(largestJpegDimen, s) && (currArea < bestArea && + currArea >= largestOutputArea)) { + bestPreviewDimen = s; + } + } + + mIntermediateBufferSize = bestPreviewDimen; + if (DEBUG) { + Log.d(TAG, "Intermediate buffer selected with dimens: " + + bestPreviewDimen.toString()); + } + } else { + mIntermediateBufferSize = null; + if (DEBUG) { + Log.d(TAG, "No Intermediate buffer selected, no preview outputs were configured"); + } + } + + + // TODO: Detect and optimize single-output paths here to skip stream teeing. if (mGLThreadManager == null) { mGLThreadManager = new GLThreadManager(mCameraId); @@ -282,7 +368,28 @@ public class RequestThreadManager { mGLThreadManager.setConfigurationAndWait(mPreviewOutputs); mGLThreadManager.allowNewFrames(); mPreviewTexture = mGLThreadManager.getCurrentSurfaceTexture(); - mPreviewTexture.setOnFrameAvailableListener(mPreviewCallback); + if (mPreviewTexture != null) { + mPreviewTexture.setOnFrameAvailableListener(mPreviewCallback); + } + } + + private static Size findLargestByArea(List<Size> sizes) { + return Collections.max(sizes, new SizeComparator()); + } + + private static boolean checkAspectRatiosMatch(Size a, Size b) { + float aAspect = a.getWidth() / (float) a.getHeight(); + float bAspect = b.getWidth() / (float) b.getHeight(); + + return Math.abs(aAspect - bAspect) < ASPECT_RATIO_TOLERANCE; + } + + private static List<Size> convertSizeList(List<Camera.Size> sizeList) { + List<Size> sizes = new ArrayList<>(sizeList.size()); + for (Camera.Size s : sizeList) { + sizes.add(new Size(s.width, s.height)); + } + return sizes; } // Calculate the highest FPS range supported @@ -376,8 +483,10 @@ public class RequestThreadManager { // TODO: err handling throw new IOError(e); } - // TODO: Set fields in result. - mDeviceState.setCaptureResult(holder, new CameraMetadataNative()); + Camera.Parameters params = mCamera.getParameters(); + CameraMetadataNative result = convertResultMetadata(params, + holder.getRequest()); + mDeviceState.setCaptureResult(holder, result); } break; case MSG_CLEANUP: @@ -397,6 +506,15 @@ public class RequestThreadManager { } }; + private CameraMetadataNative convertResultMetadata(Camera.Parameters params, + CaptureRequest request) { + CameraMetadataNative result = new CameraMetadataNative(); + result.set(CaptureResult.LENS_FOCAL_LENGTH, params.getFocalLength()); + + // TODO: Remaining result metadata tags conversions. + return result; + } + /** * Create a new RequestThreadManager. * @@ -437,6 +555,12 @@ public class RequestThreadManager { Handler handler = mRequestThread.waitAndGetHandler(); handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP)); mRequestThread.quitSafely(); + try { + mRequestThread.join(); + } catch (InterruptedException e) { + Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.", + mRequestThread.getName(), mRequestThread.getId())); + } } /** diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java index 2f0f6bc4582e..49f419f2dcf0 100644 --- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java +++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java @@ -431,6 +431,11 @@ public class SurfaceTextureRenderer { public void configureSurfaces(Collection<Surface> surfaces) { releaseEGLContext(); + if (surfaces == null || surfaces.size() == 0) { + Log.w(TAG, "No output surfaces configured for GL drawing."); + return; + } + for (Surface s : surfaces) { // If pixel conversions aren't handled by egl, use a pbuffer if (LegacyCameraDevice.needsConversion(s)) { diff --git a/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java b/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java index 40cda08cf7f7..898c74612ca6 100644 --- a/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java +++ b/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java @@ -47,6 +47,8 @@ public class CameraBinderDecorator { * - POLICY_PROHIBITS * - RESOURCE_BUSY * - NO_SUCH_DEVICE + * - NOT_SUPPORTED + * - TOO_MANY_USERS */ public static final int EACCES = -13; public static final int EBUSY = -16; diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java index 9601d4a703df..9914800ec6ca 100644 --- a/core/java/android/widget/Spinner.java +++ b/core/java/android/widget/Spinner.java @@ -25,6 +25,7 @@ import android.content.res.TypedArray; import android.database.DataSetObserver; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; @@ -427,9 +428,15 @@ public class Spinner extends AbsSpinner implements OnClickListener { * {@link Adapter#getItemViewType(int) getItemViewType(int)} on the object * returned from {@link #getAdapter()} will always return 0. Calling * {@link Adapter#getViewTypeCount() getViewTypeCount()} will always return - * 1. + * 1. On API {@link Build.VERSION_CODES#L} and above, attempting to set an + * adapter with more than one view type will throw an + * {@link IllegalArgumentException}. + * + * @param adapter the adapter to set * * @see AbsSpinner#setAdapter(SpinnerAdapter) + * @throws IllegalArgumentException if the adapter has more than one view + * type */ @Override public void setAdapter(SpinnerAdapter adapter) { @@ -437,6 +444,12 @@ public class Spinner extends AbsSpinner implements OnClickListener { mRecycler.clear(); + final int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion; + if (targetSdkVersion >= Build.VERSION_CODES.L + && adapter != null && adapter.getViewTypeCount() != 1) { + throw new IllegalArgumentException("Spinner adapter view type count must be 1"); + } + if (mPopup != null) { mPopup.setAdapter(new DropDownAdapter(adapter)); } else { diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 86207f0a3ea2..87ee6184563d 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -174,21 +174,21 @@ static int read_memtrack_memory(struct memtrack_proc* p, int pid, ssize_t pss = memtrack_proc_graphics_pss(p); if (pss < 0) { - ALOGW("failed to get graphics pss: %d", pss); + ALOGW("failed to get graphics pss: %zd", pss); return pss; } graphics_mem->graphics = pss / 1024; pss = memtrack_proc_gl_pss(p); if (pss < 0) { - ALOGW("failed to get gl pss: %d", pss); + ALOGW("failed to get gl pss: %zd", pss); return pss; } graphics_mem->gl = pss / 1024; pss = memtrack_proc_other_pss(p); if (pss < 0) { - ALOGW("failed to get other pss: %d", pss); + ALOGW("failed to get other pss: %zd", pss); return pss; } graphics_mem->other = pss / 1024; @@ -231,9 +231,9 @@ static void read_mapinfo(FILE *fp, stats_t* stats) unsigned referenced = 0; unsigned temp; - unsigned long int start; - unsigned long int end = 0; - unsigned long int prevEnd = 0; + uint64_t start; + uint64_t end = 0; + uint64_t prevEnd = 0; char* name; int name_pos; @@ -255,7 +255,7 @@ static void read_mapinfo(FILE *fp, stats_t* stats) if (len < 1) return; line[--len] = 0; - if (sscanf(line, "%lx-%lx %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) { + if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) { skip = true; } else { while (isspace(line[name_pos])) { @@ -371,7 +371,7 @@ static void read_mapinfo(FILE *fp, stats_t* stats) referenced = temp; } else if (line[0] == 'S' && sscanf(line, "Swap: %d kB", &temp) == 1) { swapped_out = temp; - } else if (strlen(line) > 30 && line[8] == '-' && line[17] == ' ') { + } else if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %*s %*x %*x:%*x %*d", &start, &end) == 2) { // looks like a new mapping // example: "10000000-10001000 ---p 10000000 00:00 0" break; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index c08c5e2ef61c..8b5dff014e19 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -657,6 +657,13 @@ android:label="@string/permlab_addVoicemail" android:description="@string/permdesc_addVoicemail" /> + <!-- Allows an application to read all the voicemails in the system. --> + <permission android:name="com.android.voicemail.permission.READ_ALL_VOICEMAIL" + android:permissionGroup="android.permission-group.VOICEMAIL" + android:protectionLevel="dangerous" + android:label="@string/permlab_readAllVoicemail" + android:description="@string/permdesc_readAllVoicemail" /> + <!-- =============================================== --> <!-- Permissions for enabling accessibility features --> <!-- =============================================== --> diff --git a/core/res/res/drawable/view_accessibility_focused.xml b/core/res/res/drawable/view_accessibility_focused.xml index 0da9a81bd8fb..68e3f1ecb97f 100644 --- a/core/res/res/drawable/view_accessibility_focused.xml +++ b/core/res/res/drawable/view_accessibility_focused.xml @@ -17,9 +17,11 @@ <shape xmlns:android="http://schemas.android.com/apk/res/android" > <stroke - android:width="2dp" - android:color="@color/accessibility_focus_highlight" /> + android:width="4dp" + android:color="@color/accessibility_focus_highlight" + android:dashWidth="4dp" + android:dashGap="2dp" /> - <corners android:radius="2dp"/> + <corners android:radius="2dp" /> </shape> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 9f6c7ad81089..f3045144718c 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -143,7 +143,7 @@ <color name="keyguard_avatar_nick_color">#ffffffff</color> <color name="keyguard_avatar_frame_pressed_color">#ff35b5e5</color> - <color name="accessibility_focus_highlight">#80ffff00</color> + <color name="accessibility_focus_highlight">#bf39b500</color> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 6e1b5ef62c47..e31dbaf08009 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2971,6 +2971,13 @@ to your voicemail inbox.</string> <!-- Title of an application permission, listed so the user can choose whether + they want to allow the application to do this. [CHAR LIMIT=NONE] --> + <string name="permlab_readAllVoicemail">read all voicemail</string> + <!-- Description of an application permission, listed so the user can choose whether + they want to allow the application to do this. [CHAR LIMIT=NONE] --> + <string name="permdesc_readAllVoicemail">Allows the app to read all your voicemails.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_writeGeolocationPermissions">modify Browser geolocation permissions</string> <!-- Description of an application permission, listed so the user can choose whether diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index 0da2b9989444..9d6d76e37118 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -23,9 +23,7 @@ import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.os.Looper; -import android.os.Process; import android.os.RemoteException; -import android.os.UserHandle; import java.io.ByteArrayInputStream; import java.io.Closeable; import java.security.InvalidKeyException; @@ -439,14 +437,6 @@ public final class KeyChain { * Caller should call unbindService on the result when finished. */ public static KeyChainConnection bind(Context context) throws InterruptedException { - return bindAsUser(context, Process.myUserHandle()); - } - - /** - * @hide - */ - public static KeyChainConnection bindAsUser(Context context, UserHandle user) - throws InterruptedException { if (context == null) { throw new NullPointerException("context == null"); } @@ -469,10 +459,9 @@ public final class KeyChain { Intent intent = new Intent(IKeyChainService.class.getName()); ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0); intent.setComponent(comp); - boolean isBound = context.bindServiceAsUser(intent, - keyChainServiceConnection, - Context.BIND_AUTO_CREATE, - user); + boolean isBound = context.bindService(intent, + keyChainServiceConnection, + Context.BIND_AUTO_CREATE); if (!isBound) { throw new AssertionError("could not bind to KeyChainService"); } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java index 8a7e64288412..7dba21da4c9d 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java @@ -22,7 +22,6 @@ import android.hardware.ICameraClient; import android.hardware.ICameraServiceListener; import android.hardware.IProCameraCallbacks; import android.hardware.IProCameraUser; -import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.ICameraDeviceCallbacks; import android.hardware.camera2.ICameraDeviceUser; import android.hardware.camera2.impl.CameraMetadataNative; @@ -51,8 +50,14 @@ import android.util.Log; * </pre> */ public class CameraBinderTest extends AndroidTestCase { + private static final int MAX_PARAMETERS_LENGTH = 100; + static String TAG = "CameraBinderTest"; + // From ICameraService.h + private static final int API_VERSION_1 = 1; + private static final int API_VERSION_2 = 2; + protected CameraBinderTestUtils mUtils; public CameraBinderTest() { @@ -95,6 +100,56 @@ public class CameraBinderTest extends AndroidTestCase { } } + @SmallTest + public void testGetLegacyParameters() throws Exception { + for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) { + + String[] parameters = new String[1]; + assertEquals("Camera service returned parameters for camera " + cameraId, + CameraBinderTestUtils.NO_ERROR, + mUtils.getCameraService().getLegacyParameters(cameraId, /*out*/parameters)); + assertNotNull(parameters[0]); + assertTrue("Parameters should have at least one character in it", + parameters[0].length() > 0); + + int end = parameters[0].length(); + if (end > MAX_PARAMETERS_LENGTH) { + end = MAX_PARAMETERS_LENGTH; + } + + Log.v(TAG, "Camera " + cameraId + " parameters: " + parameters[0].substring(0, end)); + } + } + + /** The camera2 api is only supported on HAL3.2+ devices */ + @SmallTest + public void testSupportsCamera2Api() throws Exception { + for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) { + + int res = mUtils.getCameraService().supportsCameraApi(cameraId, API_VERSION_2); + + if (res != CameraBinderTestUtils.NO_ERROR && res != CameraBinderTestUtils.EOPNOTSUPP) { + fail("Camera service returned bad value when queried if it supports camera2 api: " + + res + " for camera ID " + cameraId); + } + + boolean supports = res == CameraBinderTestUtils.NO_ERROR; + Log.v(TAG, "Camera " + cameraId + " supports api2: " + supports); + } + } + + /** The camera1 api is supported on *all* devices regardless of HAL version */ + @SmallTest + public void testSupportsCamera1Api() throws Exception { + for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) { + + int res = mUtils.getCameraService().supportsCameraApi(cameraId, API_VERSION_1); + assertEquals( + "Camera service returned bad value when queried if it supports camera1 api: " + + res + " for camera ID " + cameraId, CameraBinderTestUtils.NO_ERROR, res); + } + } + static abstract class DummyBase extends Binder implements android.os.IInterface { @Override public IBinder asBinder() { @@ -158,6 +213,7 @@ public class CameraBinderTest extends AndroidTestCase { * android.hardware.camera2.ICameraDeviceCallbacks#onCameraError(int, * android.hardware.camera2.CaptureResultExtras) */ + @Override public void onCameraError(int errorCode, CaptureResultExtras resultExtras) throws RemoteException { // TODO Auto-generated method stub @@ -170,6 +226,7 @@ public class CameraBinderTest extends AndroidTestCase { * android.hardware.camera2.ICameraDeviceCallbacks#onCaptureStarted( * android.hardware.camera2.CaptureResultExtras, long) */ + @Override public void onCaptureStarted(CaptureResultExtras resultExtras, long timestamp) throws RemoteException { // TODO Auto-generated method stub @@ -183,6 +240,7 @@ public class CameraBinderTest extends AndroidTestCase { * android.hardware.camera2.impl.CameraMetadataNative, * android.hardware.camera2.CaptureResultExtras) */ + @Override public void onResultReceived(CameraMetadataNative result, CaptureResultExtras resultExtras) throws RemoteException { // TODO Auto-generated method stub @@ -193,6 +251,7 @@ public class CameraBinderTest extends AndroidTestCase { * (non-Javadoc) * @see android.hardware.camera2.ICameraDeviceCallbacks#onCameraIdle() */ + @Override public void onCameraIdle() throws RemoteException { // TODO Auto-generated method stub diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java index 1be2a6261fcc..6be538ae3597 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java @@ -22,6 +22,7 @@ public class CameraBinderTestUtils { protected static final int INVALID_OPERATION = -38; protected static final int ALREADY_EXISTS = -17; public static final int NO_ERROR = 0; + public static final int EOPNOTSUPP = -95; private final Context mContext; public CameraBinderTestUtils(Context context) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 20684a131293..5bc23b5e4057 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -311,8 +311,27 @@ public abstract class BaseStatusBar extends SystemUI implements mHandler.post(new Runnable() { @Override public void run() { - if (mNotificationData.findByKey(sbn.getKey()) != null || - isHeadsUp(sbn.getKey())) { + Notification n = sbn.getNotification(); + boolean isUpdate = mNotificationData.findByKey(sbn.getKey()) != null + || isHeadsUp(sbn.getKey()); + boolean isGroupedChild = n.getGroup() != null + && (n.flags & Notification.FLAG_GROUP_SUMMARY) == 0; + if (isGroupedChild) { + if (DEBUG) { + Log.d(TAG, "Ignoring group child: " + sbn); + } + // Don't show grouped notifications. If this is an + // update, i.e. the notification existed before but + // wasn't a group child, remove the old instance. + // Otherwise just update the ranking. + if (isUpdate) { + removeNotificationInternal(sbn.getKey(), rankingMap); + } else { + updateRankingInternal(rankingMap); + } + return; + } + if (isUpdate) { updateNotificationInternal(sbn, rankingMap); } else { addNotificationInternal(sbn, rankingMap); diff --git a/rs/java/android/renderscript/Font.java b/rs/java/android/renderscript/Font.java index b22aeb7c04b7..4318b9d4d21c 100644 --- a/rs/java/android/renderscript/Font.java +++ b/rs/java/android/renderscript/Font.java @@ -111,10 +111,10 @@ public class Font extends BaseObj { FontFamily serifFamily = new FontFamily(); serifFamily.mNames = sSerifNames; - serifFamily.mNormalFileName = "DroidSerif-Regular.ttf"; - serifFamily.mBoldFileName = "DroidSerif-Bold.ttf"; - serifFamily.mItalicFileName = "DroidSerif-Italic.ttf"; - serifFamily.mBoldItalicFileName = "DroidSerif-BoldItalic.ttf"; + serifFamily.mNormalFileName = "NotoSerif-Regular.ttf"; + serifFamily.mBoldFileName = "NotoSerif-Bold.ttf"; + serifFamily.mItalicFileName = "NotoSerif-Italic.ttf"; + serifFamily.mBoldItalicFileName = "NotoSerif-BoldItalic.ttf"; addFamilyToMap(serifFamily); FontFamily monoFamily = new FontFamily(); diff --git a/services/core/java/com/android/server/MountServiceIdler.java b/services/core/java/com/android/server/MountServiceIdler.java index bcb6e9ee9d68..215d92d8a49b 100644 --- a/services/core/java/com/android/server/MountServiceIdler.java +++ b/services/core/java/com/android/server/MountServiceIdler.java @@ -30,8 +30,7 @@ public class MountServiceIdler extends JobService { private static final String TAG = "MountServiceIdler"; private static ComponentName sIdleService = - new ComponentName(MountServiceIdler.class.getPackage().getName(), - MountServiceIdler.class.getName()); + new ComponentName("android", MountServiceIdler.class.getName()); private static int MOUNT_JOB_ID = 808; diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 11f855e2eb46..30446c748308 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -19,6 +19,7 @@ package com.android.server.am; import android.app.ActivityManager.TaskDescription; import android.os.PersistableBundle; import android.os.Trace; + import com.android.internal.app.ResolverActivity; import com.android.internal.util.XmlUtils; import com.android.server.AttributeCache; @@ -49,6 +50,7 @@ import android.util.Slog; import android.util.TimeUtils; import android.view.IApplicationToken; import android.view.WindowManager; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; @@ -58,6 +60,7 @@ import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashSet; +import java.util.Objects; /** * An entry in the history stack, representing an activity. @@ -83,6 +86,7 @@ final class ActivityRecord { final ActivityManagerService service; // owner final IApplicationToken.Stub appToken; // window manager token final ActivityInfo info; // all about me + final ApplicationInfo appInfo; // information about activity's app final int launchedFromUid; // always the uid who started the activity. final String launchedFromPackage; // always the package who started the activity. final int userId; // Which user is this running for? @@ -103,9 +107,6 @@ final class ActivityRecord { static final int RECENTS_ACTIVITY_TYPE = 2; int mActivityType; - final String baseDir; // where activity source (resources etc) located - final String resDir; // where public activity source (public resources etc) located - final String dataDir; // where activity data should go CharSequence nonLocalizedLabel; // the label information from the package mgr. int labelRes; // the label information from the package mgr. int icon; // resource identifier of activity's icon. @@ -184,11 +185,13 @@ final class ActivityRecord { pw.print(prefix); pw.print("taskAffinity="); pw.println(taskAffinity); pw.print(prefix); pw.print("realActivity="); pw.println(realActivity.flattenToShortString()); - pw.print(prefix); pw.print("baseDir="); pw.println(baseDir); - if (!resDir.equals(baseDir)) { - pw.print(prefix); pw.print("resDir="); pw.println(resDir); + if (appInfo != null) { + pw.print(prefix); pw.print("baseDir="); pw.println(appInfo.sourceDir); + if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) { + pw.print(prefix); pw.print("resDir="); pw.println(appInfo.publicSourceDir); + } + pw.print(prefix); pw.print("dataDir="); pw.println(appInfo.dataDir); } - pw.print(prefix); pw.print("dataDir="); pw.println(dataDir); pw.print(prefix); pw.print("stateNotNeeded="); pw.print(stateNotNeeded); pw.print(" componentSpecified="); pw.print(componentSpecified); pw.print(" mActivityType="); pw.println(mActivityType); @@ -418,9 +421,7 @@ final class ActivityRecord { taskAffinity = aInfo.taskAffinity; stateNotNeeded = (aInfo.flags& ActivityInfo.FLAG_STATE_NOT_NEEDED) != 0; - baseDir = aInfo.applicationInfo.sourceDir; - resDir = aInfo.applicationInfo.publicSourceDir; - dataDir = aInfo.applicationInfo.dataDir; + appInfo = aInfo.applicationInfo; nonLocalizedLabel = aInfo.nonLocalizedLabel; labelRes = aInfo.labelRes; if (nonLocalizedLabel == null && labelRes == 0) { @@ -488,9 +489,7 @@ final class ActivityRecord { realActivity = null; taskAffinity = null; stateNotNeeded = false; - baseDir = null; - resDir = null; - dataDir = null; + appInfo = null; processName = null; packageName = null; fullscreen = true; diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index e54c95ee16fb..f79c02601c4d 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -45,6 +45,7 @@ import android.util.TimeUtils; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * A running application service. @@ -70,9 +71,6 @@ final class ServiceRecord extends Binder { final String packageName; // the package implementing intent's component final String processName; // process where this component wants to run final String permission;// permission needed to access service - final String baseDir; // where activity source (resources etc) located - final String resDir; // where public activity source (public resources etc) located - final String dataDir; // where activity data should go final boolean exported; // from ServiceInfo.exported final Runnable restarter; // used to schedule retries of starting the service final long createTime; // when this service was created @@ -209,11 +207,13 @@ final class ServiceRecord extends Binder { } long now = SystemClock.uptimeMillis(); long nowReal = SystemClock.elapsedRealtime(); - pw.print(prefix); pw.print("baseDir="); pw.println(baseDir); - if (!resDir.equals(baseDir)) { - pw.print(prefix); pw.print("resDir="); pw.println(resDir); + if (appInfo != null) { + pw.print(prefix); pw.print("baseDir="); pw.println(appInfo.sourceDir); + if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) { + pw.print(prefix); pw.print("resDir="); pw.println(appInfo.publicSourceDir); + } + pw.print(prefix); pw.print("dataDir="); pw.println(appInfo.dataDir); } - pw.print(prefix); pw.print("dataDir="); pw.println(dataDir); pw.print(prefix); pw.print("app="); pw.println(app); if (isolatedProc != null) { pw.print(prefix); pw.print("isolatedProc="); pw.println(isolatedProc); @@ -305,9 +305,6 @@ final class ServiceRecord extends Binder { packageName = sInfo.applicationInfo.packageName; processName = sInfo.processName; permission = sInfo.permission; - baseDir = sInfo.applicationInfo.sourceDir; - resDir = sInfo.applicationInfo.publicSourceDir; - dataDir = sInfo.applicationInfo.dataDir; exported = sInfo.exported; this.restarter = restarter; createTime = SystemClock.elapsedRealtime(); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 8d943963185f..5feee50bad20 100755 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -42,6 +42,7 @@ import com.android.internal.app.ResolverActivity; import com.android.internal.content.NativeLibraryHelper; import com.android.internal.content.NativeLibraryHelper.ApkHandle; import com.android.internal.content.PackageHelper; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; @@ -2242,11 +2243,12 @@ public class PackageManagerService extends IPackageManager.Stub { if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) == 0) { return null; } + // App code is gone, so we aren't worried about split paths pkg = new PackageParser.Package(packageName); pkg.applicationInfo.packageName = packageName; pkg.applicationInfo.flags = ps.pkgFlags | ApplicationInfo.FLAG_IS_DATA_ONLY; - pkg.applicationInfo.publicSourceDir = ps.resourcePathString; pkg.applicationInfo.sourceDir = ps.codePathString; + pkg.applicationInfo.publicSourceDir = ps.resourcePathString; pkg.applicationInfo.dataDir = getDataPathForPackage(packageName, 0).getPath(); pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString; @@ -4081,6 +4083,7 @@ public class PackageManagerService extends IPackageManager.Stub { return false; } final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); + // TODO: generate idmap for split APKs if (mInstaller.idmap(pkg.codePath, opkg.codePath, sharedGid) != 0) { Slog.e(TAG, "Failed to generate idmap for " + pkg.codePath + " and " + opkg.codePath); return false; @@ -4362,23 +4365,30 @@ public class PackageManagerService extends IPackageManager.Stub { } } - String codePath = null; + final String codePath = pkg.codePath; + final String[] splitCodePaths = pkg.splitCodePaths; + String resPath = null; + String[] splitResPaths = null; if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0 && !updatedPkgBetter) { if (ps != null && ps.resourcePathString != null) { resPath = ps.resourcePathString; + splitResPaths = deriveSplitResPaths(pkg.splitCodePaths); } else { // Should not happen at all. Just log an error. Slog.e(TAG, "Resource path not set for pkg : " + pkg.packageName); } } else { resPath = pkg.codePath; + splitResPaths = pkg.splitCodePaths; } - codePath = pkg.codePath; // Set application objects path explicitly. pkg.applicationInfo.sourceDir = codePath; pkg.applicationInfo.publicSourceDir = resPath; + pkg.applicationInfo.splitSourceDirs = splitCodePaths; + pkg.applicationInfo.splitPublicSourceDirs = splitResPaths; + // Note that we invoke the following method only if we are about to unpack an application PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE, currentTime, user, abiOverride); @@ -4626,52 +4636,56 @@ public class PackageManagerService extends IPackageManager.Stub { } } - boolean performed = false; - if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { - String path = pkg.codePath; - try { - boolean isDexOptNeededInternal = DexFile.isDexOptNeededInternal(path, - pkg.packageName, - instructionSet, - defer); - // There are three basic cases here: - // 1.) we need to dexopt, either because we are forced or it is needed - // 2.) we are defering a needed dexopt - // 3.) we are skipping an unneeded dexopt - if (forceDex || (!defer && isDexOptNeededInternal)) { - Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName); - final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); - int ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg), - pkg.packageName, instructionSet); - // Note that we ran dexopt, since rerunning will - // probably just result in an error again. - pkg.mDexOptNeeded = false; - if (ret < 0) { - return DEX_OPT_FAILED; + if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) { + final ArrayList<String> paths = new ArrayList<>(); + paths.add(pkg.codePath); + if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { + Collections.addAll(paths, pkg.splitCodePaths); + } + + for (String path : paths) { + try { + boolean isDexOptNeededInternal = DexFile.isDexOptNeededInternal(path, + pkg.packageName, instructionSet, defer); + // There are three basic cases here: + // 1.) we need to dexopt, either because we are forced or it is needed + // 2.) we are defering a needed dexopt + // 3.) we are skipping an unneeded dexopt + if (forceDex || (!defer && isDexOptNeededInternal)) { + Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName); + final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); + int ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg), + pkg.packageName, instructionSet); + // Note that we ran dexopt, since rerunning will + // probably just result in an error again. + pkg.mDexOptNeeded = false; + if (ret < 0) { + return DEX_OPT_FAILED; + } + return DEX_OPT_PERFORMED; } - return DEX_OPT_PERFORMED; - } - if (defer && isDexOptNeededInternal) { - if (mDeferredDexOpt == null) { - mDeferredDexOpt = new HashSet<PackageParser.Package>(); + if (defer && isDexOptNeededInternal) { + if (mDeferredDexOpt == null) { + mDeferredDexOpt = new HashSet<PackageParser.Package>(); + } + mDeferredDexOpt.add(pkg); + return DEX_OPT_DEFERRED; } - mDeferredDexOpt.add(pkg); - return DEX_OPT_DEFERRED; + pkg.mDexOptNeeded = false; + return DEX_OPT_SKIPPED; + } catch (FileNotFoundException e) { + Slog.w(TAG, "Apk not found for dexopt: " + path); + return DEX_OPT_FAILED; + } catch (IOException e) { + Slog.w(TAG, "IOException reading apk: " + path, e); + return DEX_OPT_FAILED; + } catch (StaleDexCacheError e) { + Slog.w(TAG, "StaleDexCacheError when reading apk: " + path, e); + return DEX_OPT_FAILED; + } catch (Exception e) { + Slog.w(TAG, "Exception when doing dexopt : ", e); + return DEX_OPT_FAILED; } - pkg.mDexOptNeeded = false; - return DEX_OPT_SKIPPED; - } catch (FileNotFoundException e) { - Slog.w(TAG, "Apk not found for dexopt: " + path); - return DEX_OPT_FAILED; - } catch (IOException e) { - Slog.w(TAG, "IOException reading apk: " + path, e); - return DEX_OPT_FAILED; - } catch (StaleDexCacheError e) { - Slog.w(TAG, "StaleDexCacheError when reading apk: " + path, e); - return DEX_OPT_FAILED; - } catch (Exception e) { - Slog.w(TAG, "Exception when doing dexopt : ", e); - return DEX_OPT_FAILED; } } return DEX_OPT_SKIPPED; @@ -4819,6 +4833,9 @@ public class PackageManagerService extends IPackageManager.Stub { } if (p != null) { usesLibraryFiles.add(p.codePath); + if (!ArrayUtils.isEmpty(p.splitCodePaths)) { + Collections.addAll(usesLibraryFiles, p.splitCodePaths); + } } } @@ -4906,7 +4923,7 @@ public class PackageManagerService extends IPackageManager.Stub { private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags, int scanMode, long currentTime, UserHandle user, String abiOverride) { final File scanFile = new File(pkg.codePath); - if (scanFile == null || pkg.applicationInfo.sourceDir == null || + if (pkg.applicationInfo.sourceDir == null || pkg.applicationInfo.publicSourceDir == null) { // Bail out. The resource and code paths haven't been set. Slog.w(TAG, " Code and resource paths haven't been set correctly"); @@ -5355,6 +5372,7 @@ public class PackageManagerService extends IPackageManager.Stub { * only for non-system apps and system app upgrades. */ if (pkg.applicationInfo.nativeLibraryDir != null) { + // TODO: extend to extract native code from split APKs final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile); try { // Enable gross and lame hacks for apps that are built with old @@ -5908,6 +5926,8 @@ public class PackageManagerService extends IPackageManager.Stub { a.info.packageName = pkg.applicationInfo.packageName; a.info.sourceDir = pkg.applicationInfo.sourceDir; a.info.publicSourceDir = pkg.applicationInfo.publicSourceDir; + a.info.splitSourceDirs = pkg.applicationInfo.splitSourceDirs; + a.info.splitPublicSourceDirs = pkg.applicationInfo.splitPublicSourceDirs; a.info.dataDir = pkg.applicationInfo.dataDir; a.info.nativeLibraryDir = pkg.applicationInfo.nativeLibraryDir; mInstrumentation.put(a.getComponentName(), a); @@ -9021,6 +9041,10 @@ public class PackageManagerService extends IPackageManager.Stub { abstract boolean doPostDeleteLI(boolean delete); abstract boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException; + String[] getSplitCodePaths() { + return null; + } + /** * Called before the source arguments are copied. This is used mostly * for MoveParams when it needs to read the source file to put it in the @@ -9109,10 +9133,6 @@ public class PackageManagerService extends IPackageManager.Stub { } } - String getCodePath() { - return codeFileName; - } - void createCopyFile() { installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir; codeFileName = createTempPackageFile(installDir).getPath(); @@ -9268,10 +9288,6 @@ public class PackageManagerService extends IPackageManager.Stub { return status; } - String getResourcePath() { - return resourceFileName; - } - private String getResourcePathFromCodePath() { final String codePath = getCodePath(); if (isFwdLocked()) { @@ -9302,6 +9318,16 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override + String getCodePath() { + return codeFileName; + } + + @Override + String getResourcePath() { + return resourceFileName; + } + + @Override String getNativeLibraryPath() { if (libraryPath == null) { libraryPath = getLibraryPathFromCodePath(); @@ -9768,6 +9794,20 @@ public class PackageManagerService extends IPackageManager.Stub { return codePath.substring(sidx+1, eidx); } + private static String[] deriveSplitResPaths(String[] splitCodePaths) { + String[] splitResPaths = null; + if (!ArrayUtils.isEmpty(splitCodePaths)) { + splitResPaths = new String[splitCodePaths.length]; + for (int i = 0; i < splitCodePaths.length; i++) { + final String splitCodePath = splitCodePaths[i]; + final String resName = getApkName(splitCodePath) + ".zip"; + splitResPaths[i] = new File(new File(splitCodePath).getParentFile(), + resName).getAbsolutePath(); + } + } + return splitResPaths; + } + class PackageInstalledInfo { String name; int uid; @@ -10067,6 +10107,7 @@ public class PackageManagerService extends IPackageManager.Stub { // Utility method used to move dex files during install. private int moveDexFilesLI(String oldCodePath, PackageParser.Package newPackage) { + // TODO: extend to move split APK dex files if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { final String instructionSet = getAppInstructionSet(newPackage.applicationInfo); int retCode = mInstaller.movedex(oldCodePath, newPackage.codePath, @@ -10296,6 +10337,9 @@ public class PackageManagerService extends IPackageManager.Stub { pkg.codePath = args.getCodePath(); pkg.applicationInfo.sourceDir = args.getCodePath(); pkg.applicationInfo.publicSourceDir = args.getResourcePath(); + pkg.applicationInfo.splitSourceDirs = args.getSplitCodePaths(); + pkg.applicationInfo.splitPublicSourceDirs = deriveSplitResPaths( + pkg.applicationInfo.splitSourceDirs); pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath(); if (replace) { replacePackageLI(pkg, parseFlags, scanMode, args.user, diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 24a998d24e0c..4574caf20322 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -1324,7 +1324,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private void manageMonitoringCertificateNotification(Intent intent) { final NotificationManager notificationManager = getNotificationManager(); - final boolean hasCert = !(new TrustedCertificateStore().userAliases().isEmpty()); + final boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled(); if (! hasCert) { if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) { for (UserInfo user : mUserManager.getUsers()) { @@ -2384,19 +2384,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return !"".equals(state); } - public boolean installCaCert(ComponentName who, byte[] certBuffer) throws RemoteException { - if (who == null) { - mContext.enforceCallingOrSelfPermission(MANAGE_CA_CERTIFICATES, null); - } else { - synchronized (this) { - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); - } - } - + public boolean installCaCert(byte[] certBuffer) throws RemoteException { + mContext.enforceCallingOrSelfPermission(MANAGE_CA_CERTIFICATES, null); + KeyChainConnection keyChainConnection = null; byte[] pemCert; try { X509Certificate cert = parseCert(certBuffer); - pemCert = Credentials.convertToPem(cert); + pemCert = Credentials.convertToPem(cert); } catch (CertificateException ce) { Log.e(LOG_TAG, "Problem converting cert", ce); return false; @@ -2404,24 +2398,20 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Log.e(LOG_TAG, "Problem reading cert", ioe); return false; } - - final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId()); - final long id = Binder.clearCallingIdentity(); try { - final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle); + keyChainConnection = KeyChain.bind(mContext); try { keyChainConnection.getService().installCaCertificate(pemCert); return true; - } catch (RemoteException e) { - Log.e(LOG_TAG, "installCaCertsToKeyChain(): ", e); } finally { - keyChainConnection.close(); + if (keyChainConnection != null) { + keyChainConnection.close(); + keyChainConnection = null; + } } } catch (InterruptedException e1) { Log.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1); Thread.currentThread().interrupt(); - } finally { - Binder.restoreCallingIdentity(id); } return false; } @@ -2433,31 +2423,34 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { certBuffer)); } - public void uninstallCaCert(ComponentName who, String alias) { - if (who == null) { - mContext.enforceCallingOrSelfPermission(MANAGE_CA_CERTIFICATES, null); - } else { - synchronized (this) { - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); - } + public void uninstallCaCert(final byte[] certBuffer) { + mContext.enforceCallingOrSelfPermission(MANAGE_CA_CERTIFICATES, null); + TrustedCertificateStore certStore = new TrustedCertificateStore(); + String alias = null; + try { + X509Certificate cert = parseCert(certBuffer); + alias = certStore.getCertificateAlias(cert); + } catch (CertificateException ce) { + Log.e(LOG_TAG, "Problem creating X509Certificate", ce); + return; + } catch (IOException ioe) { + Log.e(LOG_TAG, "Problem reading certificate", ioe); + return; } - - final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId()); - final long id = Binder.clearCallingIdentity(); try { - final KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, userHandle); + KeyChainConnection keyChainConnection = KeyChain.bind(mContext); + IKeyChainService service = keyChainConnection.getService(); try { - keyChainConnection.getService().deleteCaCertificate(alias); + service.deleteCaCertificate(alias); } catch (RemoteException e) { Log.e(LOG_TAG, "from CaCertUninstaller: ", e); } finally { keyChainConnection.close(); + keyChainConnection = null; } } catch (InterruptedException ie) { Log.w(LOG_TAG, "CaCertUninstaller: ", ie); Thread.currentThread().interrupt(); - } finally { - Binder.restoreCallingIdentity(id); } } diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java index d97450900a95..8a4e12388c97 100644 --- a/telecomm/java/android/telecomm/ConnectionService.java +++ b/telecomm/java/android/telecomm/ConnectionService.java @@ -20,15 +20,9 @@ import android.net.Uri; import android.os.Bundle; import android.telephony.DisconnectCause; -import android.os.SystemClock; - import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; import java.util.Map; -import java.util.Set; /** * A {@link android.app.Service} that provides telephone connections to diff --git a/telecomm/java/android/telecomm/Subscription.aidl b/telecomm/java/android/telecomm/Subscription.aidl new file mode 100644 index 000000000000..6327fcc15dd8 --- /dev/null +++ b/telecomm/java/android/telecomm/Subscription.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telecomm; + +/** + * {@hide} + */ +parcelable Subscription; diff --git a/telecomm/java/android/telecomm/Subscription.java b/telecomm/java/android/telecomm/Subscription.java index f187f4d176be..964db4a5c05b 100644 --- a/telecomm/java/android/telecomm/Subscription.java +++ b/telecomm/java/android/telecomm/Subscription.java @@ -16,25 +16,169 @@ package android.telecomm; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; +import android.telephony.Rlog; +import android.util.DisplayMetrics; +import android.util.Log; + +import java.util.MissingResourceException; /** * Represents a distinct subscription, line of service or call placement method that - * a {@link ConnectionService} can use to place phone calls. + * the system can use to place phone calls. */ public class Subscription implements Parcelable { - public Subscription() {} + private static final int NO_DENSITY = -1; + + private static final String LOG_TAG = "Subscription"; + + private final ComponentName mComponentName; + private final String mId; + private final Uri mHandle; + private final int mLabelResId; + private final int mShortDescriptionResId; + private final int mIconResId; + private final boolean mIsEnabled; + private final boolean mIsSystemDefault; + + public Subscription( + ComponentName componentName, + String id, + Uri handle, + int labelResId, + int shortDescriptionResId, + int iconResId, + boolean isEnabled, + boolean isSystemDefault) { + mComponentName = componentName; + mId = id; + mHandle = handle; + mLabelResId = labelResId; + mShortDescriptionResId = shortDescriptionResId; + mIconResId = iconResId; + mIsSystemDefault = isSystemDefault; + mIsEnabled = isEnabled; + } + + /** + * The {@code ComponentName} of the {@link android.telecomm.ConnectionService} which is + * responsible for making phone calls using this {@code Subscription}. + * + * @return A suitable {@code ComponentName}. + */ + public ComponentName getComponentName() { + return mComponentName; + } + + /** + * A unique identifier for this {@code Subscription}, generated by and meaningful to the + * {@link android.telecomm.ConnectionService} that created it. + * + * @return A unique identifier for this {@code Subscription}. + */ + public String getId() { + return mId; + } + + /** + * The handle (e.g., a phone number) associated with this {@code Subscription}. This represents + * the destination from which outgoing calls using this {@code Subscription} will appear to come + * from, if applicable, and the destination to which incoming calls using this + * {@code Subscription} may be addressed. + * + * @return A handle expressed as a {@code Uri}, for example, a phone number. + */ + public Uri getHandle() { + return mHandle; + } + + /** + * A short string label describing this {@code Subscription}. + * + * @param context The invoking {@code Context}, used for retrieving resources. + * + * @return A label for this {@code Subscription}. + */ + public String getLabel(Context context) { + return getString(context, mLabelResId); + } + + /** + * A short paragraph describing this {@code Subscription}. + * + * @param context The invoking {@code Context}, used for retrieving resources. + * + * @return A description for this {@code Subscription}. + */ + public String getShortDescription(Context context) { + return getString(context, mShortDescriptionResId); + } + + /** + * An icon to represent this {@code Subscription} in a user interface. + * + * @param context The invoking {@code Context}, used for retrieving resources. + * + * @return An icon for this {@code Subscription}. + */ + public Drawable getIcon(Context context) { + return getIcon(context, mIconResId, NO_DENSITY); + } + + /** + * An icon to represent this {@code Subscription} in a user interface. + * + * @param context The invoking {@code Context}, used for retrieving resources. + * @param density A display density from {@link DisplayMetrics}. + * + * @return An icon for this {@code Subscription}. + */ + public Drawable getIcon(Context context, int density) { + return getIcon(context, mIconResId, density); + } + + /** + * Whether this {@code Subscription} is enabled for use. + * + * @return {@code true} if this {@code Subscription} is enabled. + */ + public boolean isEnabled() { + return mIsEnabled; + } + + /** + * Whether this {@code Subscription} is the system default. + * + * @return {@code true} if this {@code Subscription} is the system default. + */ + public boolean isSystemDefault() { + return mIsSystemDefault; + } public int describeContents() { return 0; } - public void writeToParcel(Parcel out, int flags) {} + public void writeToParcel(Parcel out, int flags) { + out.writeParcelable(mComponentName, flags); + out.writeString(mId); + out.writeString(mHandle != null ? mHandle.toString() : ""); + out.writeInt(mLabelResId); + out.writeInt(mShortDescriptionResId); + out.writeInt(mIconResId); + out.writeInt(mIsEnabled ? 1 : 0); + out.writeInt(mIsSystemDefault ? 1 : 0); + } - public static final Parcelable.Creator<Subscription> CREATOR - = new Parcelable.Creator<Subscription>() { + public static final Creator<Subscription> CREATOR + = new Creator<Subscription>() { public Subscription createFromParcel(Parcel in) { return new Subscription(in); } @@ -44,5 +188,54 @@ public class Subscription implements Parcelable { } }; - private Subscription(Parcel in) {} + private Subscription(Parcel in) { + mComponentName = in.readParcelable(getClass().getClassLoader()); + mId = in.readString(); + String uriString = in.readString(); + mHandle = uriString.length() > 0 ? Uri.parse(uriString) : null; + mLabelResId = in.readInt(); + mShortDescriptionResId = in.readInt(); + mIconResId = in.readInt(); + mIsEnabled = in.readInt() == 1; + mIsSystemDefault = in.readInt() == 1; + } + + private String getString(Context context, int resId) { + Context packageContext; + try { + packageContext = context.createPackageContext(mComponentName.getPackageName(), 0); + } catch (PackageManager.NameNotFoundException e) { + if (Rlog.isLoggable(LOG_TAG, Log.WARN)) { + Rlog.w(LOG_TAG, "Cannot find package " + mComponentName.getPackageName()); + } + return null; + } + String result = packageContext.getString(resId); + if (result == null && Rlog.isLoggable(LOG_TAG, Log.WARN)) { + Rlog.w(LOG_TAG, "Cannot find string " + resId + " in package " + + mComponentName.getPackageName()); + } + return result; + } + + private Drawable getIcon(Context context, int resId, int density) { + Context packageContext; + try { + packageContext = context.createPackageContext(mComponentName.getPackageName(), 0); + } catch (PackageManager.NameNotFoundException e) { + if (Rlog.isLoggable(LOG_TAG, Log.WARN)) { + Rlog.w(LOG_TAG, "Cannot find package " + mComponentName.getPackageName()); + } + return null; + } + try { + return density == NO_DENSITY ? + packageContext.getResources().getDrawable(resId) : + packageContext.getResources().getDrawableForDensity(resId, density); + } catch (MissingResourceException e) { + Rlog.e(LOG_TAG, "Cannot find icon " + resId + " in package " + + mComponentName.getPackageName() + ": " + e.toString()); + return null; + } + } } diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl index 638b86aa149c..dc2b8694c66e 100644 --- a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl +++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl @@ -16,6 +16,7 @@ package com.android.internal.telecomm; +import android.telecomm.Subscription; import android.content.ComponentName; /** @@ -45,4 +46,19 @@ interface ITelecommService { * Returns the component name of the phone application installed on the system partition. */ ComponentName getSystemPhoneApplication(); + + /** + * Gets a list of Subscriptions. + */ + List<Subscription> getSubscriptions(); + + /** + * Sets the enabled state of a given Subscription. + */ + void setEnabled(in Subscription subscription, boolean enabled); + + /** + * Sets a given Subscription as the system default. + */ + void setSystemDefault(in Subscription subscription); } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 50bbb1edc962..3fde36e9b666 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -19,15 +19,12 @@ package android.telephony; import android.annotation.SystemApi; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.content.ComponentName; import android.content.Context; import android.os.Bundle; -import android.os.Handler; -import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; -import android.telephony.Rlog; +import android.telecomm.Subscription; import android.util.Log; import com.android.internal.telecomm.ITelecommService; @@ -40,7 +37,6 @@ import com.android.internal.telephony.TelephonyProperties; import java.io.FileInputStream; import java.io.IOException; -import java.util.HashMap; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -302,6 +298,17 @@ public class TelephonyManager { public static final String EXTRA_INCOMING_NUMBER = "incoming_number"; /** + * The lookup key used with an {@link android.content.Intent#ACTION_CALL} or + * {@link android.content.Intent#ACTION_DIAL} {@code Intent} for a {@link Subscription} + * object indicating a preference when making a phone connection. + * + * <p class="note"> + * Retrieve with + * {@link android.content.Intent#getParcelableExtra(String)}. + */ + public static final String EXTRA_SUBSCRIPTION = "subscription"; + + /** * Broadcast intent action indicating that a precise call state * (cellular) on the device has changed. * @@ -3112,4 +3119,40 @@ public class TelephonyManager { } return false; } + + /** + * Return a list of Subscriptions that can be used to indicate a preference when making + * a phone call. + * + * @see #EXTRA_SUBSCRIPTION + * @return A list of {@code Subscription} objects. + */ + public List<Subscription> getSubscriptions() { + try { + return getTelecommService().getSubscriptions(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#getSubscriptions", e); + } + return null; + } + + /** @hide */ + @SystemApi + public void setEnabled(Subscription subscription, boolean enabled) { + try { + getTelecommService().setEnabled(subscription, enabled); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#setEnabled", e); + } + } + + /** @hide */ + @SystemApi + public void setSystemDefault(Subscription subscription) { + try { + getTelecommService().setSystemDefault(subscription); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#setSystemDefault", e); + } + } } |