diff options
Diffstat (limited to 'core/java')
218 files changed, 16327 insertions, 732 deletions
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index 9ef13de..eb59f6b 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -329,6 +329,7 @@ public class AccountManager { * @return The account's password, null if none or if the account doesn't exist */ public String getPassword(final Account account) { + android.util.SeempLog.record(22); if (account == null) throw new IllegalArgumentException("account is null"); try { return mService.getPassword(account); @@ -357,6 +358,7 @@ public class AccountManager { * @return The user data, null if the account or key doesn't exist */ public String getUserData(final Account account, final String key) { + android.util.SeempLog.record(23); if (account == null) throw new IllegalArgumentException("account is null"); if (key == null) throw new IllegalArgumentException("key is null"); try { @@ -567,6 +569,7 @@ public class AccountManager { if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); return new Future2Task<String>(handler, callback) { public void doWork() throws RemoteException { + android.util.SeempLog.record(31); mService.getAuthTokenLabel(mResponse, accountType, authTokenType); } @@ -612,6 +615,7 @@ public class AccountManager { if (features == null) throw new IllegalArgumentException("features is null"); return new Future2Task<Boolean>(handler, callback) { public void doWork() throws RemoteException { + android.util.SeempLog.record(31); mService.hasFeatures(mResponse, account, features, mContext.getOpPackageName()); } public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException { @@ -664,6 +668,7 @@ public class AccountManager { if (type == null) throw new IllegalArgumentException("type is null"); return new Future2Task<Account[]>(handler, callback) { public void doWork() throws RemoteException { + android.util.SeempLog.record(31); mService.getAccountsByFeatures(mResponse, type, features, mContext.getOpPackageName()); } @@ -707,6 +712,7 @@ public class AccountManager { * already exists, the account is null, or another error occurs. */ public boolean addAccountExplicitly(Account account, String password, Bundle userdata) { + android.util.SeempLog.record(24); if (account == null) throw new IllegalArgumentException("account is null"); try { return mService.addAccountExplicitly(account, password, userdata); @@ -777,6 +783,7 @@ public class AccountManager { return new Future2Task<Account>(handler, callback) { @Override public void doWork() throws RemoteException { + android.util.SeempLog.record(31); mService.renameAccount(mResponse, account, newName); } @Override @@ -837,10 +844,12 @@ public class AccountManager { @Deprecated public AccountManagerFuture<Boolean> removeAccount(final Account account, AccountManagerCallback<Boolean> callback, Handler handler) { + android.util.SeempLog.record(25); if (account == null) throw new IllegalArgumentException("account is null"); return new Future2Task<Boolean>(handler, callback) { @Override public void doWork() throws RemoteException { + android.util.SeempLog.record(31); mService.removeAccount(mResponse, account, false); } @Override @@ -896,10 +905,12 @@ public class AccountManager { */ public AccountManagerFuture<Bundle> removeAccount(final Account account, final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) { + android.util.SeempLog.record(28); if (account == null) throw new IllegalArgumentException("account is null"); return new AmsTask(activity, handler, callback) { @Override public void doWork() throws RemoteException { + android.util.SeempLog.record(34); mService.removeAccount(mResponse, account, activity != null); } }.start(); @@ -921,6 +932,7 @@ public class AccountManager { return new Future2Task<Boolean>(handler, callback) { @Override public void doWork() throws RemoteException { + android.util.SeempLog.record(31); mService.removeAccountAsUser(mResponse, account, false, userHandle.getIdentifier()); } @Override @@ -946,6 +958,7 @@ public class AccountManager { throw new IllegalArgumentException("userHandle is null"); return new AmsTask(activity, handler, callback) { public void doWork() throws RemoteException { + android.util.SeempLog.record(34); mService.removeAccountAsUser(mResponse, account, activity != null, userHandle.getIdentifier()); } @@ -1062,6 +1075,7 @@ public class AccountManager { * @param password The password to set, null to clear the password */ public void setPassword(final Account account, final String password) { + android.util.SeempLog.record(26); if (account == null) throw new IllegalArgumentException("account is null"); try { mService.setPassword(account, password); @@ -1091,6 +1105,7 @@ public class AccountManager { * @param account The account whose password to clear */ public void clearPassword(final Account account) { + android.util.SeempLog.record(27); if (account == null) throw new IllegalArgumentException("account is null"); try { mService.clearPassword(account); @@ -1119,6 +1134,7 @@ public class AccountManager { * @param value String value to set, {@code null} to clear this user data key */ public void setUserData(final Account account, final String key, final String value) { + android.util.SeempLog.record(28); if (account == null) throw new IllegalArgumentException("account is null"); if (key == null) throw new IllegalArgumentException("key is null"); try { @@ -1270,6 +1286,7 @@ public class AccountManager { optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName()); return new AmsTask(activity, handler, callback) { public void doWork() throws RemoteException { + android.util.SeempLog.record(31); mService.getAuthToken(mResponse, account, authTokenType, false /* notifyOnAuthFailure */, true /* expectActivityLaunch */, optionsIn); @@ -1438,6 +1455,7 @@ public class AccountManager { optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName()); return new AmsTask(null, handler, callback) { public void doWork() throws RemoteException { + android.util.SeempLog.record(31); mService.getAuthToken(mResponse, account, authTokenType, notifyAuthFailure, false /* expectActivityLaunch */, optionsIn); } @@ -1498,6 +1516,7 @@ public class AccountManager { final String authTokenType, final String[] requiredFeatures, final Bundle addAccountOptions, final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) { + android.util.SeempLog.record(29); if (accountType == null) throw new IllegalArgumentException("accountType is null"); final Bundle optionsIn = new Bundle(); if (addAccountOptions != null) { @@ -1507,6 +1526,7 @@ public class AccountManager { return new AmsTask(activity, handler, callback) { public void doWork() throws RemoteException { + android.util.SeempLog.record(31); mService.addAccount(mResponse, accountType, authTokenType, requiredFeatures, activity != null, optionsIn); } @@ -1531,6 +1551,7 @@ public class AccountManager { return new AmsTask(activity, handler, callback) { public void doWork() throws RemoteException { + android.util.SeempLog.record(31); mService.addAccountAsUser(mResponse, accountType, authTokenType, requiredFeatures, activity != null, optionsIn, userHandle.getIdentifier()); } @@ -1578,6 +1599,7 @@ public class AccountManager { return new Future2Task<Boolean>(handler, callback) { @Override public void doWork() throws RemoteException { + android.util.SeempLog.record(34); mService.copyAccountToUser( mResponse, account, UserHandle.USER_OWNER, user.getIdentifier()); } @@ -1705,6 +1727,7 @@ public class AccountManager { final int userId = userHandle.getIdentifier(); return new AmsTask(activity, handler, callback) { public void doWork() throws RemoteException { + android.util.SeempLog.record(31); mService.confirmCredentialsAsUser(mResponse, account, options, activity != null, userId); } @@ -1817,9 +1840,11 @@ public class AccountManager { public AccountManagerFuture<Bundle> editProperties(final String accountType, final Activity activity, final AccountManagerCallback<Bundle> callback, final Handler handler) { + android.util.SeempLog.record(30); if (accountType == null) throw new IllegalArgumentException("accountType is null"); return new AmsTask(activity, handler, callback) { public void doWork() throws RemoteException { + android.util.SeempLog.record(31); mService.editProperties(mResponse, accountType, activity != null); } }.start(); @@ -2175,6 +2200,7 @@ public class AccountManager { private volatile int mNumAccounts = 0; public void doWork() throws RemoteException { + android.util.SeempLog.record(31); getAccountsByTypeAndFeatures(mAccountType, mFeatures, new AccountManagerCallback<Account[]>() { public void run(AccountManagerFuture<Account[]> future) { diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 87c9efc2..0ae9187 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -41,6 +41,7 @@ import android.content.pm.ConfigurationInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.PackageManager; import android.content.pm.UserInfo; +import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Color; @@ -59,6 +60,7 @@ import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Size; import android.util.Slog; + import org.xmlpull.v1.XmlSerializer; import java.io.FileDescriptor; @@ -525,8 +527,16 @@ public class ActivityManager { * @hide */ static public boolean isHighEndGfx() { - return !isLowRamDeviceStatic() && - !Resources.getSystem().getBoolean(com.android.internal.R.bool.config_avoidGfxAccel); + return (!isLowRamDeviceStatic() && + !Resources.getSystem().getBoolean(com.android.internal.R.bool.config_avoidGfxAccel)) + || isForcedHighEndGfx(); + } + + /** + * @hide + */ + public static boolean isForcedHighEndGfx() { + return SystemProperties.getBoolean("persist.sys.force_highendgfx", false); } /** @@ -1316,6 +1326,23 @@ public class ActivityManager { } /** + * Check whether the current foreground tasks belongs to a given package. + * + * @param packageName Name of the package to check for + * + * @return Whether the current foreground tasks belongs to the given package + * @hide + */ + public boolean isPackageInForeground(String packageName) { + try { + return ActivityManagerNative.getDefault().isPackageInForeground(packageName); + } catch (RemoteException e) { + // System dead, we will be dead too soon! + return false; + } + } + + /** * Completely remove the given task. * * @param taskId Identifier of the task to be removed. @@ -2298,6 +2325,16 @@ public class ActivityManager { return null; } } + /** + * @hide + */ + public Configuration getConfiguration() { + try { + return ActivityManagerNative.getDefault().getConfiguration(); + } catch (RemoteException e) { + return null; + } + } /** * Sets the memory trim mode for a process and schedules a memory trim operation. @@ -2988,4 +3025,17 @@ public class ActivityManager { } } } + + /** + * @throws SecurityException Throws SecurityException if the caller does + * not hold the {@link android.Manifest.permission#CHANGE_CONFIGURATION} permission. + * + * @hide + */ + public void updateConfiguration(Configuration values) throws SecurityException { + try { + ActivityManagerNative.getDefault().updateConfiguration(values); + } catch (RemoteException e) { + } + } } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index b9d2595..bfd6b5a 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -577,6 +577,15 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case GET_CALLING_PACKAGE_FOR_BROADCAST_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + boolean foreground = data.readInt() == 1 ? true : false; + String res = getCallingPackageForBroadcast(foreground); + reply.writeNoException(); + reply.writeString(res); + return true; + } + case GET_CALLING_ACTIVITY_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder token = data.readStrongBinder(); @@ -638,6 +647,15 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case IS_PACKAGE_IN_FOREGROUND_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + String packageName = data.readString(); + boolean result = isPackageInForeground(packageName); + reply.writeNoException(); + reply.writeInt(result ? 1 : 0); + return true; + } + case GET_RECENT_TASKS_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); int maxNum = data.readInt(); @@ -2097,7 +2115,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM case KEYGUARD_GOING_AWAY_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); - keyguardGoingAway(data.readInt() != 0, data.readInt() != 0); + keyguardGoingAway(data.readInt() != 0, data.readInt() != 0, data.readInt() != 0); reply.writeNoException(); return true; } @@ -3193,6 +3211,19 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); return res; } + public String getCallingPackageForBroadcast(boolean foreground) throws RemoteException + { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeInt(foreground ? 1 : 0); + mRemote.transact(GET_CALLING_PACKAGE_FOR_BROADCAST_TRANSACTION, data, reply, 0); + reply.readException(); + String res = reply.readString(); + data.recycle(); + reply.recycle(); + return res; + } public ComponentName getCallingActivity(IBinder token) throws RemoteException { Parcel data = Parcel.obtain(); @@ -3279,6 +3310,18 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); return list; } + public boolean isPackageInForeground(String packageName) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeString(packageName); + mRemote.transact(IS_PACKAGE_IN_FOREGROUND_TRANSACTION, data, reply, 0); + reply.readException(); + boolean result = reply.readInt() != 0; + data.recycle(); + reply.recycle(); + return result; + } public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags, int userId) throws RemoteException { Parcel data = Parcel.obtain(); @@ -5265,12 +5308,14 @@ class ActivityManagerProxy implements IActivityManager } public void keyguardGoingAway(boolean disableWindowAnimations, - boolean keyguardGoingToNotificationShade) throws RemoteException { + boolean keyguardGoingToNotificationShade, + boolean keyguardShowingMedia) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeInt(disableWindowAnimations ? 1 : 0); data.writeInt(keyguardGoingToNotificationShade ? 1 : 0); + data.writeInt(keyguardShowingMedia ? 1 : 0); mRemote.transact(KEYGUARD_GOING_AWAY_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index da21eaf..782dc46 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -46,6 +46,7 @@ import android.database.sqlite.SQLiteDebug; import android.database.sqlite.SQLiteDebug.DbStats; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Typeface; import android.hardware.display.DisplayManagerGlobal; import android.net.ConnectivityManager; import android.net.IConnectivityManager; @@ -78,6 +79,7 @@ import android.os.Trace; import android.os.UserHandle; import android.provider.Settings; import android.security.NetworkSecurityPolicy; +import android.text.TextUtils; import android.util.AndroidRuntimeException; import android.util.ArrayMap; import android.util.DisplayMetrics; @@ -90,6 +92,7 @@ import android.util.Slog; import android.util.SuperNotCalledException; import android.view.Display; import android.view.HardwareRenderer; +import android.view.InflateException; import android.view.View; import android.view.ViewDebug; import android.view.ViewManager; @@ -893,9 +896,25 @@ public final class ActivityThread { public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent, int resultCode, String dataStr, Bundle extras, boolean ordered, boolean sticky, int sendingUser, int processState) throws RemoteException { - updateProcessState(processState, false); - receiver.performReceive(intent, resultCode, dataStr, extras, ordered, - sticky, sendingUser); + RemoteException remoteException = null; + if (!Binder.isProxy(receiver)) { + updateProcessState(processState, false); + try { + receiver.performReceive(intent, resultCode, dataStr, extras, ordered, + sticky, sendingUser); + return; + } catch (RemoteException e) { + remoteException = e; + } + } + if (ordered) { + Slog.w(TAG, receiver + " is no longer alive"); + ActivityManagerNative.getDefault().finishReceiver(receiver.asBinder(), + resultCode, dataStr, extras, true, intent.getFlags()); + if (remoteException != null) { + throw remoteException; + } + } } @Override @@ -1697,9 +1716,21 @@ public final class ActivityThread { */ Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs, String[] libDirs, int displayId, Configuration overrideConfiguration, - LoadedApk pkgInfo) { + LoadedApk pkgInfo, Context context, String pkgName) { return mResourcesManager.getTopLevelResources(resDir, splitResDirs, overlayDirs, libDirs, - displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo()); + displayId, pkgName, overrideConfiguration, pkgInfo.getCompatibilityInfo(), context, + pkgInfo.getApplicationInfo().isThemeable); + } + + /** + * Creates the top level resources for the given package. + */ + Resources getTopLevelThemedResources(String resDir, int displayId, + Configuration overrideConfiguration, LoadedApk pkgInfo, + String pkgName, String themePkgName) { + return mResourcesManager.getTopLevelThemedResources(resDir, displayId, pkgName, + themePkgName, pkgInfo.getCompatibilityInfo(), + pkgInfo.getApplicationInfo().isThemeable); } final Handler getHandler() { @@ -1817,8 +1848,7 @@ public final class ActivityThread { } LoadedApk packageInfo = ref != null ? ref.get() : null; - if (packageInfo == null || (packageInfo.mResources != null - && !packageInfo.mResources.getAssets().isUpToDate())) { + if (packageInfo == null) { if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package " : "Loading resource-only package ") + aInfo.packageName + " (in " + (mBoundApplication != null @@ -1844,6 +1874,10 @@ public final class ActivityThread { new WeakReference<LoadedApk>(packageInfo)); } } + if (packageInfo.mResources != null + && !packageInfo.mResources.getAssets().isUpToDate()) { + packageInfo.mResources = null; + } return packageInfo; } } @@ -4235,6 +4269,11 @@ public final class ActivityThread { configDiff = mConfiguration.updateFrom(config); config = applyCompatConfiguration(mCurDefaultDisplayDpi); + + final Theme systemTheme = getSystemContext().getTheme(); + if ((systemTheme.getChangingConfigurations() & configDiff) != 0) { + systemTheme.rebase(); + } } ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config); @@ -4253,8 +4292,12 @@ public final class ActivityThread { if (configDiff != 0) { // Ask text layout engine to free its caches if there is a locale change boolean hasLocaleConfigChange = ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0); - if (hasLocaleConfigChange) { + boolean hasFontConfigChange = ((configDiff & ActivityInfo.CONFIG_THEME_FONT) != 0); + if (hasLocaleConfigChange || hasFontConfigChange) { Canvas.freeTextLayoutCaches(); + if (hasFontConfigChange) { + Typeface.recreateDefaults(); + } if (DEBUG_CONFIGURATION) Slog.v(TAG, "Cleared TextLayout Caches"); } } @@ -4423,7 +4466,7 @@ public final class ActivityThread { + DisplayMetrics.DENSITY_DEVICE + " to " + mCurDefaultDisplayDpi); DisplayMetrics.DENSITY_DEVICE = mCurDefaultDisplayDpi; - Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEFAULT); + Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEVICE); } } @@ -4523,7 +4566,8 @@ public final class ActivityThread { } - final boolean is24Hr = "24".equals(mCoreSettings.getString(Settings.System.TIME_12_24)); + final boolean is24Hr = android.text.format.DateFormat.is24HourFormat( + mCoreSettings.getString(Settings.System.TIME_12_24), data.config.locale); DateFormat.set24HourTimePref(is24Hr); View.mDebugViewAttributes = diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index 9c0d931..c075ed6 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -24,8 +24,6 @@ import android.content.IIntentSender; import android.content.Intent; import android.content.IntentSender; import android.graphics.SurfaceTexture; -import android.os.Handler; -import android.os.HandlerThread; import android.os.IBinder; import android.os.Message; import android.os.OperationCanceledException; @@ -45,6 +43,17 @@ import android.view.WindowManager; import dalvik.system.CloseGuard; import java.lang.ref.WeakReference; +import java.util.ArrayDeque; +import java.util.concurrent.Executor; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import com.android.internal.annotations.GuardedBy; + /** @hide */ public class ActivityView extends ViewGroup { @@ -53,9 +62,64 @@ public class ActivityView extends ViewGroup { private static final int MSG_SET_SURFACE = 1; - DisplayMetrics mMetrics = new DisplayMetrics(); + private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); + private static final int MINIMUM_POOL_SIZE = 1; + private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; + private static final int KEEP_ALIVE = 1; + + private static final ThreadFactory sThreadFactory = new ThreadFactory() { + private final AtomicInteger mCount = new AtomicInteger(1); + + public Thread newThread(Runnable r) { + return new Thread(r, "ActivityView #" + mCount.getAndIncrement()); + } + }; + + private static final BlockingQueue<Runnable> sPoolWorkQueue = + new LinkedBlockingQueue<Runnable>(128); + + /** + * An {@link Executor} that can be used to execute tasks in parallel. + */ + private static final Executor sExecutor = new ThreadPoolExecutor(MINIMUM_POOL_SIZE, + MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); + + + private static class SerialExecutor implements Executor { + private final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); + private Runnable mActive; + + public synchronized void execute(final Runnable r) { + mTasks.offer(new Runnable() { + public void run() { + try { + r.run(); + } finally { + scheduleNext(); + } + } + }); + if (mActive == null) { + scheduleNext(); + } + } + + protected synchronized void scheduleNext() { + if ((mActive = mTasks.poll()) != null) { + sExecutor.execute(mActive); + } + } + } + + private final SerialExecutor mExecutor = new SerialExecutor(); + + private final int mDensityDpi; private final TextureView mTextureView; + + @GuardedBy("mActivityContainerLock") private ActivityContainerWrapper mActivityContainer; + private Object mActivityContainerLock = new Object(); + private Activity mActivity; private int mWidth; private int mHeight; @@ -63,8 +127,6 @@ public class ActivityView extends ViewGroup { private int mLastVisibility; private ActivityViewCallback mActivityViewCallback; - private HandlerThread mThread = new HandlerThread("ActivityViewThread"); - private Handler mHandler; public ActivityView(Context context) { this(context, null); @@ -97,28 +159,14 @@ public class ActivityView extends ViewGroup { + e); } - mThread.start(); - mHandler = new Handler(mThread.getLooper()) { - @Override - public void handleMessage(Message msg) { - super.handleMessage(msg); - if (msg.what == MSG_SET_SURFACE) { - try { - mActivityContainer.setSurface((Surface) msg.obj, msg.arg1, msg.arg2, - mMetrics.densityDpi); - } catch (RemoteException e) { - throw new RuntimeException( - "ActivityView: Unable to set surface of ActivityContainer. " + e); - } - } - } - }; mTextureView = new TextureView(context); mTextureView.setSurfaceTextureListener(new ActivityViewSurfaceTextureListener()); addView(mTextureView); WindowManager wm = (WindowManager)mActivity.getSystemService(Context.WINDOW_SERVICE); - wm.getDefaultDisplay().getMetrics(mMetrics); + DisplayMetrics metrics = new DisplayMetrics(); + wm.getDefaultDisplay().getMetrics(metrics); + mDensityDpi = metrics.densityDpi; mLastVisibility = getVisibility(); @@ -131,15 +179,13 @@ public class ActivityView extends ViewGroup { } @Override - protected void onVisibilityChanged(View changedView, int visibility) { + protected void onVisibilityChanged(View changedView, final int visibility) { super.onVisibilityChanged(changedView, visibility); if (mSurface != null && (visibility == View.GONE || mLastVisibility == View.GONE)) { - Message msg = Message.obtain(mHandler, MSG_SET_SURFACE); - msg.obj = (visibility == View.GONE) ? null : mSurface; - msg.arg1 = mWidth; - msg.arg2 = mHeight; - mHandler.sendMessage(msg); + if (DEBUG) Log.v(TAG, "visibility changed; enqueing runnable"); + final Surface surface = (visibility == View.GONE) ? null : mSurface; + setSurfaceAsync(surface, mWidth, mHeight, mDensityDpi, false); } mLastVisibility = visibility; } @@ -230,8 +276,10 @@ public class ActivityView extends ViewGroup { Log.e(TAG, "Duplicate call to release"); return; } - mActivityContainer.release(); - mActivityContainer = null; + synchronized (mActivityContainerLock) { + mActivityContainer.release(); + mActivityContainer = null; + } if (mSurface != null) { mSurface.release(); @@ -241,21 +289,37 @@ public class ActivityView extends ViewGroup { mTextureView.setSurfaceTextureListener(null); } - private void attachToSurfaceWhenReady() { - final SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture(); - if (surfaceTexture == null || mSurface != null) { - // Either not ready to attach, or already attached. - return; - } - - mSurface = new Surface(surfaceTexture); - try { - mActivityContainer.setSurface(mSurface, mWidth, mHeight, mMetrics.densityDpi); - } catch (RemoteException e) { - mSurface.release(); - mSurface = null; - throw new RuntimeException("ActivityView: Unable to create ActivityContainer. " + e); - } + private void setSurfaceAsync(final Surface surface, final int width, final int height, + final int densityDpi, final boolean callback) { + mExecutor.execute(new Runnable() { + public void run() { + try { + synchronized (mActivityContainerLock) { + if (mActivityContainer != null) { + mActivityContainer.setSurface(surface, width, height, densityDpi); + } + } + } catch (RemoteException e) { + throw new RuntimeException( + "ActivityView: Unable to set surface of ActivityContainer. ", + e); + } + if (callback) { + post(new Runnable() { + @Override + public void run() { + if (mActivityViewCallback != null) { + if (surface != null) { + mActivityViewCallback.onSurfaceAvailable(ActivityView.this); + } else { + mActivityViewCallback.onSurfaceDestroyed(ActivityView.this); + } + } + } + }); + } + } + }); } /** @@ -306,10 +370,8 @@ public class ActivityView extends ViewGroup { + height); mWidth = width; mHeight = height; - attachToSurfaceWhenReady(); - if (mActivityViewCallback != null) { - mActivityViewCallback.onSurfaceAvailable(ActivityView.this); - } + mSurface = new Surface(surfaceTexture); + setSurfaceAsync(mSurface, mWidth, mHeight, mDensityDpi, true); } @Override @@ -329,15 +391,7 @@ public class ActivityView extends ViewGroup { if (DEBUG) Log.d(TAG, "onSurfaceTextureDestroyed"); mSurface.release(); mSurface = null; - try { - mActivityContainer.setSurface(null, mWidth, mHeight, mMetrics.densityDpi); - } catch (RemoteException e) { - throw new RuntimeException( - "ActivityView: Unable to set surface of ActivityContainer. " + e); - } - if (mActivityViewCallback != null) { - mActivityViewCallback.onSurfaceDestroyed(ActivityView.this); - } + setSurfaceAsync(null, mWidth, mHeight, mDensityDpi, true); return true; } diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java index 330d730..b0afe7c 100644 --- a/core/java/android/app/AlarmManager.java +++ b/core/java/android/app/AlarmManager.java @@ -100,6 +100,14 @@ public class AlarmManager { */ public static final int ELAPSED_REALTIME = 3; + /** @hide + * Alarm time in {@link System#currentTimeMillis System.currentTimeMillis()} + * (wall clock time in UTC), which will wake up the device when + * it goes off. And it will power on the devices when it shuts down. + * Set as 5 to make it be compatible with android_alarm_type. + */ + public static final int RTC_POWEROFF_WAKEUP = 5; + /** * Broadcast Action: Sent after the value returned by * {@link #getNextAlarmClock()} has changed. diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 09c0a6e..11a5ee8 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1,4 +1,7 @@ /* + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Not a Contribution. + * * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,6 +31,7 @@ import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; +import android.os.SystemProperties; import android.os.UserManager; import android.util.ArrayMap; @@ -69,6 +73,10 @@ public class AppOpsManager { * will do this for you). */ + /** {@hide */ + public static final String ACTION_SU_SESSION_CHANGED = + "android.intent.action.SU_SESSION_CHANGED"; + final Context mContext; final IAppOpsService mService; final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers @@ -103,9 +111,18 @@ public class AppOpsManager { */ public static final int MODE_DEFAULT = 3; + /** + * @hide Result from {@link #checkOp}, {@link #noteOp}, {@link #startOp}: + * AppOps Service should show a dialog box on screen to get user + * permission. + */ + public static final int MODE_ASK = 4; + // when adding one of these: // - increment _NUM_OP - // - add rows to sOpToSwitch, sOpToString, sOpNames, sOpPerms, sOpDefaultMode + // - add rows to sOpToSwitch, sOpToString, sOpNames, sOpPerms, sOpDefaultMode, sOpDefaultStrictMode, + // sOpToOpString, sOpStrictMode. + // - add descriptive strings to frameworks/base/core/res/res/values/config.xml // - add descriptive strings to Settings/res/values/arrays.xml // - add the op to the appropriate template in AppOpsState.OpsTemplate (settings app) @@ -237,8 +254,20 @@ public class AppOpsManager { public static final int OP_TURN_SCREEN_ON = 61; /** @hide Get device accounts. */ public static final int OP_GET_ACCOUNTS = 62; + /** @hide Wifi state change **/ + public static final int OP_WIFI_CHANGE = 63; + /** @hide */ + public static final int OP_BLUETOOTH_CHANGE = 64; /** @hide */ - public static final int _NUM_OP = 63; + public static final int OP_BOOT_COMPLETED = 65; + /** @hide */ + public static final int OP_NFC_CHANGE = 66; + /** @hide */ + public static final int OP_DATA_CONNECT_CHANGE = 67; + /** @hide */ + public static final int OP_SU = 68; + /** @hide */ + public static final int _NUM_OP = 69; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -336,6 +365,19 @@ public class AppOpsManager { /** @hide Get device accounts. */ public static final String OPSTR_GET_ACCOUNTS = "android:get_accounts"; + /** @hide **/ + private static final String OPSTR_WIFI_CHANGE = + "android:wifi_change"; + private static final String OPSTR_BLUETOOTH_CHANGE = + "android:bluetooth_change"; + private static final String OPSTR_BOOT_COMPLETED = + "android:boot_completed"; + private static final String OPSTR_NFC_CHANGE = + "android:nfc_change"; + private static final String OPSTR_DATA_CONNECT_CHANGE = + "android:data_connect_change"; + private static final String OPSTR_SU = + "android:su"; /** * This maps each operation to the operation that serves as the @@ -356,7 +398,7 @@ public class AppOpsManager { OP_WRITE_CALL_LOG, OP_READ_CALENDAR, OP_WRITE_CALENDAR, - OP_COARSE_LOCATION, + OP_WIFI_SCAN, OP_POST_NOTIFICATION, OP_COARSE_LOCATION, OP_CALL_PHONE, @@ -409,6 +451,12 @@ public class AppOpsManager { OP_WRITE_EXTERNAL_STORAGE, OP_TURN_SCREEN_ON, OP_GET_ACCOUNTS, + OP_WIFI_CHANGE, + OP_BLUETOOTH_CHANGE, + OP_BOOT_COMPLETED, + OP_NFC_CHANGE, + OP_DATA_CONNECT_CHANGE, + OP_SU }; /** @@ -478,7 +526,13 @@ public class AppOpsManager { OPSTR_READ_EXTERNAL_STORAGE, OPSTR_WRITE_EXTERNAL_STORAGE, null, - OPSTR_GET_ACCOUNTS + OPSTR_GET_ACCOUNTS, + OPSTR_WIFI_CHANGE, + OPSTR_BLUETOOTH_CHANGE, + OPSTR_BOOT_COMPLETED, + OPSTR_NFC_CHANGE, + OPSTR_DATA_CONNECT_CHANGE, + OPSTR_SU, }; /** @@ -549,6 +603,12 @@ public class AppOpsManager { "WRITE_EXTERNAL_STORAGE", "TURN_ON_SCREEN", "GET_ACCOUNTS", + "WIFI_CHANGE", + "BLUETOOTH_CHANGE", + "BOOT_COMPLETED", + "NFC_CHANGE", + "DATA_CONNECT_CHANGE", + "SU", }; /** @@ -566,7 +626,7 @@ public class AppOpsManager { android.Manifest.permission.WRITE_CALL_LOG, android.Manifest.permission.READ_CALENDAR, android.Manifest.permission.WRITE_CALENDAR, - android.Manifest.permission.ACCESS_WIFI_STATE, + null, // no permission for wifi scan available null, // no permission required for notifications null, // neighboring cells shares the coarse location perm android.Manifest.permission.CALL_PHONE, @@ -618,7 +678,13 @@ public class AppOpsManager { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, null, // no permission for turning the screen on - Manifest.permission.GET_ACCOUNTS + Manifest.permission.GET_ACCOUNTS, + Manifest.permission.CHANGE_WIFI_STATE, + null, + Manifest.permission.RECEIVE_BOOT_COMPLETED, + Manifest.permission.NFC, + Manifest.permission.MODIFY_PHONE_STATE, + null, }; /** @@ -690,6 +756,12 @@ public class AppOpsManager { null, // WRITE_EXTERNAL_STORAGE null, // TURN_ON_SCREEN null, // GET_ACCOUNTS + null, //WIFI_CHANGE + null, //BLUETOOTH_CHANGE + null, //BOOT_COMPLETED + null, //NFC_CHANGE + null, //DATA_CONNECT_CHANGE + UserManager.DISALLOW_SU, //SU TODO: this should really be investigated. }; /** @@ -760,6 +832,12 @@ public class AppOpsManager { false, // WRITE_EXTERNAL_STORAGE false, // TURN_ON_SCREEN false, // GET_ACCOUNTS + true, // WIFI_CHANGE + true, // BLUETOOTH_CHANGE + true, // BOOT_COMPLETED + true, // NFC_CHANGE + true, //DATA_CONNECT_CHANGE + false, //SU }; /** @@ -829,6 +907,163 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, // OP_TURN_ON_SCREEN AppOpsManager.MODE_ALLOWED, + AppOpsManager.MODE_ALLOWED, // OP_WIFI_CHANGE + AppOpsManager.MODE_ALLOWED, // OP_BLUETOOTH_CHANGE + AppOpsManager.MODE_ALLOWED, // OP_BOOT_COMPLETED + AppOpsManager.MODE_ALLOWED, // OP_NFC_CHANGE + AppOpsManager.MODE_ALLOWED, + AppOpsManager.MODE_ASK, // OP_SU + }; + + /** + * This specifies the default mode for each strict operation. + */ + + private static int[] sOpDefaultStrictMode = new int[] { + AppOpsManager.MODE_ASK, // OP_COARSE_LOCATION + AppOpsManager.MODE_ASK, // OP_FINE_LOCATION + AppOpsManager.MODE_ASK, // OP_GPS + AppOpsManager.MODE_ALLOWED, // OP_VIBRATE + AppOpsManager.MODE_ASK, // OP_READ_CONTACTS + AppOpsManager.MODE_ASK, // OP_WRITE_CONTACTS + AppOpsManager.MODE_ASK, // OP_READ_CALL_LOG + AppOpsManager.MODE_ASK, // OP_WRITE_CALL_LOG + AppOpsManager.MODE_ALLOWED, // OP_READ_CALENDAR + AppOpsManager.MODE_ALLOWED, // OP_WRITE_CALENDAR + AppOpsManager.MODE_ASK, // OP_WIFI_SCAN + AppOpsManager.MODE_ALLOWED, // OP_POST_NOTIFICATION + AppOpsManager.MODE_ALLOWED, // OP_NEIGHBORING_CELLS + AppOpsManager.MODE_ASK, // OP_CALL_PHONE + AppOpsManager.MODE_ASK, // OP_READ_SMS + AppOpsManager.MODE_ASK, // OP_WRITE_SMS + AppOpsManager.MODE_ASK, // OP_RECEIVE_SMS + AppOpsManager.MODE_ALLOWED, // OP_RECEIVE_EMERGECY_SMS + AppOpsManager.MODE_ASK, // OP_RECEIVE_MMS + AppOpsManager.MODE_ALLOWED, // OP_RECEIVE_WAP_PUSH + AppOpsManager.MODE_ASK, // OP_SEND_SMS + AppOpsManager.MODE_ALLOWED, // OP_READ_ICC_SMS + AppOpsManager.MODE_ALLOWED, // OP_WRITE_ICC_SMS + AppOpsManager.MODE_ALLOWED, // OP_WRITE_SETTINGS + AppOpsManager.MODE_ALLOWED, // OP_SYSTEM_ALERT_WINDOW + AppOpsManager.MODE_ALLOWED, // OP_ACCESS_NOTIFICATIONS + AppOpsManager.MODE_ASK, // OP_CAMERA + AppOpsManager.MODE_ASK, // OP_RECORD_AUDIO + AppOpsManager.MODE_ALLOWED, // OP_PLAY_AUDIO + AppOpsManager.MODE_ALLOWED, // OP_READ_CLIPBOARD + AppOpsManager.MODE_ALLOWED, // OP_WRITE_CLIPBOARD + AppOpsManager.MODE_ALLOWED, // OP_TAKE_MEDIA_BUTTONS + AppOpsManager.MODE_ALLOWED, // OP_TAKE_AUDIO_FOCUS + AppOpsManager.MODE_ALLOWED, // OP_AUDIO_MASTER_VOLUME + AppOpsManager.MODE_ALLOWED, // OP_AUDIO_VOICE_VOLUME + AppOpsManager.MODE_ALLOWED, // OP_AUDIO_RING_VOLUME + AppOpsManager.MODE_ALLOWED, // OP_AUDIO_MEDIA_VOLUME + AppOpsManager.MODE_ALLOWED, // OP_AUDIO_ALARM_VOLUME + AppOpsManager.MODE_ALLOWED, // OP_AUDIO_NOTIFICATION_VOLUME + AppOpsManager.MODE_ALLOWED, // OP_AUDIO_BLUETOOTH_VOLUME + AppOpsManager.MODE_ALLOWED, // OP_WAKE_LOCK + AppOpsManager.MODE_ALLOWED, // OP_MONITOR_LOCATION + AppOpsManager.MODE_ASK, // OP_MONITOR_HIGH_POWER_LOCATION + AppOpsManager.MODE_DEFAULT, // OP_GET_USAGE_STATS + AppOpsManager.MODE_ALLOWED, // OP_MUTE_MICROPHONE + AppOpsManager.MODE_ALLOWED, // OP_TOAST_WINDOW + AppOpsManager.MODE_IGNORED, // OP_PROJECT_MEDIA + AppOpsManager.MODE_IGNORED, // OP_ACTIVATE_VPN + AppOpsManager.MODE_ALLOWED, // OP WALLPAPER + AppOpsManager.MODE_ALLOWED, // OP_ASSIST_STRUCTURE + AppOpsManager.MODE_ALLOWED, // OP_ASSIST_SCREENSHOT + AppOpsManager.MODE_ALLOWED, // OP_READ_PHONE_STATE + AppOpsManager.MODE_ALLOWED, // OP_ADD_VOICEMAIL + AppOpsManager.MODE_ALLOWED, // OP_USE_SIP + AppOpsManager.MODE_ALLOWED, // OP_PROCESS_OUTGOING_CALLS + AppOpsManager.MODE_ALLOWED, // OP_USE_FINGERPRINT + AppOpsManager.MODE_ALLOWED, // OP_BODY_SENSORS + AppOpsManager.MODE_ALLOWED, // OP_READ_CELL_BROADCASTS + AppOpsManager.MODE_ERRORED, // OP_MOCK_LOCATION + AppOpsManager.MODE_ALLOWED, // OP_READ_EXTERNAL_STORAGE + AppOpsManager.MODE_ALLOWED, // OP_WRITE_EXTERNAL_STORAGE + AppOpsManager.MODE_ALLOWED, // OP_TURN_ON_SCREEN + AppOpsManager.MODE_ALLOWED, // OP_GET_ACCOUNTS + AppOpsManager.MODE_ASK, // OP_WIFI_CHANGE + AppOpsManager.MODE_ASK, // OP_BLUETOOTH_CHANGE + AppOpsManager.MODE_ALLOWED, // OP_BOOT_COMPLETED + AppOpsManager.MODE_ASK, // OP_NFC_CHANGE + AppOpsManager.MODE_ASK, // OP_DATA_CONNECT_CHANGE + AppOpsManager.MODE_ASK, // OP_SU + }; + + /** + * This specifies if operation is in strict mode. + */ + private final static boolean[] sOpStrictMode = new boolean[] { + true, // OP_COARSE_LOCATION + true, // OP_FINE_LOCATION + true, // OP_GPS + false, // OP_VIBRATE + true, // OP_READ_CONTACTS + true, // OP_WRITE_CONTACTS + true, // OP_READ_CALL_LOG + true, // OP_WRITE_CALL_LOG + false, // OP_READ_CALENDAR + false, // OP_WRITE_CALENDAR + true, // OP_WIFI_SCAN + false, // OP_POST_NOTIFICATION + false, // OP_NEIGHBORING_CELLS + true, // OP_CALL_PHONE + true, // OP_READ_SMS + true, // OP_WRITE_SMS + false, // OP_RECEIVE_SMS + false, // OP_RECEIVE_EMERGECY_SMS + true, // OP_RECEIVE_MMS + false, // OP_RECEIVE_WAP_PUSH + true, // OP_SEND_SMS + false, // OP_READ_ICC_SMS + false, // OP_WRITE_ICC_SMS + false, // OP_WRITE_SETTINGS + false, // OP_SYSTEM_ALERT_WINDOW + false, // OP_ACCESS_NOTIFICATIONS + true, // OP_CAMERA + true, // OP_RECORD_AUDIO + false, // OP_PLAY_AUDIO + false, // OP_READ_CLIPBOARD + false, // OP_WRITE_CLIPBOARD + false, // OP_TAKE_MEDIA_BUTTONS + false, // OP_TAKE_AUDIO_FOCUS + false, // OP_AUDIO_MASTER_VOLUME + false, // OP_AUDIO_VOICE_VOLUME + false, // OP_AUDIO_RING_VOLUME + false, // OP_AUDIO_MEDIA_VOLUME + false, // OP_AUDIO_ALARM_VOLUME + false, // OP_AUDIO_NOTIFICATION_VOLUME + false, // OP_AUDIO_BLUETOOTH_VOLUME + false, // OP_WAKE_LOCK + false, // OP_MONITOR_LOCATION + true, // OP_MONITOR_HIGH_POWER_LOCATION + false, // OP_GET_USAGE_STATS + false, // OP_MUTE_MICROPHONE + false, // OP_TOAST_WINDOW + false, // OP_PROJECT_MEDIA + false, // OP_ACTIVATE_VPN + true, // OP WALLPAPER + false, //ASSIST_STRUCTURE + false, //ASSIST_SCREENSHOT + false, //READ_PHONE_STATE + false, //ADD_VOICEMAIL + false, // USE_SIP + false, // PROCESS_OUTGOING_CALLS + false, // USE_FINGERPRINT + false, // BODY_SENSORS + false, // READ_CELL_BROADCASTS + false, // MOCK_LOCATION + true, // READ_EXTERNAL_STORAGE + true, // WRITE_EXTERNAL_STORAGE + false, // TURN_ON_SCREEN + false, // GET_ACCOUNTS + true, // OP_WIFI_CHANGE + true, // OP_BLUETOOTH_CHANGE + false, // OP_BOOT_COMPLETED + true, // OP_NFC_CHANGE + true, // OP_DATA_CONNECT_CHANGE + true, // OP_SU }; /** @@ -901,7 +1136,13 @@ public class AppOpsManager { false, false, false, - false + false, + false, // OP_WIFI_CHANGE + false, // OP_BLUETOOTH_CHANGE + false, // OP_BOOT_COMPLETED + false, // OP_NFC_CHANGE + false, // OP_DATA_CONNECT_CHANGE + false, // OP_SU }; /** @@ -914,6 +1155,8 @@ public class AppOpsManager { */ private static HashMap<String, Integer> sPermToOp = new HashMap<>(); + private static HashMap<String, Integer> sNameToOp = new HashMap<String, Integer>(); + static { if (sOpToSwitch.length != _NUM_OP) { throw new IllegalStateException("sOpToSwitch length " + sOpToSwitch.length @@ -935,6 +1178,10 @@ public class AppOpsManager { throw new IllegalStateException("sOpDefaultMode length " + sOpDefaultMode.length + " should be " + _NUM_OP); } + if (sOpDefaultStrictMode.length != _NUM_OP) { + throw new IllegalStateException("sOpDefaultStrictMode length " + sOpDefaultStrictMode.length + + " should be " + _NUM_OP); + } if (sOpDisableReset.length != _NUM_OP) { throw new IllegalStateException("sOpDisableReset length " + sOpDisableReset.length + " should be " + _NUM_OP); @@ -947,6 +1194,10 @@ public class AppOpsManager { throw new IllegalStateException("sOpAllowSYstemRestrictionsBypass length " + sOpRestrictions.length + " should be " + _NUM_OP); } + if (sOpStrictMode.length != _NUM_OP) { + throw new IllegalStateException("sOpStrictMode length " + + sOpStrictMode.length + " should be " + _NUM_OP); + } for (int i=0; i<_NUM_OP; i++) { if (sOpToString[i] != null) { sOpStrToOp.put(sOpToString[i], i); @@ -957,6 +1208,9 @@ public class AppOpsManager { sPermToOp.put(sOpPerms[i], i); } } + for (int i=0; i<_NUM_OP; i++) { + sNameToOp.put(sOpNames[i], i); + } } /** @@ -989,6 +1243,15 @@ public class AppOpsManager { } /** + * Map a non-localized name for the operation back to the Op number + * @hide + */ + public static int nameToOp(String name) { + Integer val = sNameToOp.get(name); + return val != null ? val : OP_NONE; + } + + /** * Retrieve the permission associated with an operation, or null if there is not one. * @hide */ @@ -1026,7 +1289,9 @@ public class AppOpsManager { * Retrieve the default mode for the operation. * @hide */ - public static int opToDefaultMode(int op) { + public static int opToDefaultMode(int op, boolean isStrict) { + if (isStrict) + return sOpDefaultStrictMode[op]; return sOpDefaultMode[op]; } @@ -1113,9 +1378,11 @@ public class AppOpsManager { private final int mDuration; private final int mProxyUid; private final String mProxyPackageName; + private final int mAllowedCount; + private final int mIgnoredCount; public OpEntry(int op, int mode, long time, long rejectTime, int duration, - int proxyUid, String proxyPackage) { + int proxyUid, String proxyPackage, int allowedCount, int ignoredCount) { mOp = op; mMode = mode; mTime = time; @@ -1123,6 +1390,8 @@ public class AppOpsManager { mDuration = duration; mProxyUid = proxyUid; mProxyPackageName = proxyPackage; + mAllowedCount = allowedCount; + mIgnoredCount = ignoredCount; } public int getOp() { @@ -1157,6 +1426,14 @@ public class AppOpsManager { return mProxyPackageName; } + public int getAllowedCount() { + return mAllowedCount; + } + + public int getIgnoredCount() { + return mIgnoredCount; + } + @Override public int describeContents() { return 0; @@ -1171,6 +1448,8 @@ public class AppOpsManager { dest.writeInt(mDuration); dest.writeInt(mProxyUid); dest.writeString(mProxyPackageName); + dest.writeInt(mAllowedCount); + dest.writeInt(mIgnoredCount); } OpEntry(Parcel source) { @@ -1181,6 +1460,8 @@ public class AppOpsManager { mDuration = source.readInt(); mProxyUid = source.readInt(); mProxyPackageName = source.readString(); + mAllowedCount = source.readInt(); + mIgnoredCount = source.readInt(); } public static final Creator<OpEntry> CREATOR = new Creator<OpEntry>() { @@ -1753,4 +2034,75 @@ public class AppOpsManager { public void finishOp(int op) { finishOp(op, Process.myUid(), mContext.getOpPackageName()); } + + /** @hide */ + public static boolean isStrictEnable() { + return SystemProperties.getBoolean("persist.sys.strict_op_enable", false); + } + + /** + * Check if op in strict mode + * @hide + */ + public static boolean isStrictOp(int code) { + return sOpStrictMode[code]; + } + + + /** @hide */ + public static int stringToMode(String permission) { + if ("allowed".equalsIgnoreCase(permission)) { + return AppOpsManager.MODE_ALLOWED; + } else if ("ignored".equalsIgnoreCase(permission)) { + return AppOpsManager.MODE_IGNORED; + } else if ("ask".equalsIgnoreCase(permission)) { + return AppOpsManager.MODE_ASK; + } + return AppOpsManager.MODE_ERRORED; + } + + /** @hide */ + public static int stringOpToOp (String op) { + Integer val = sOpStrToOp.get(op); + if (val == null) { + val = OP_NONE; + } + return val; + } + + /** @hide */ + public boolean isControlAllowed(int op, String packageName) { + boolean isShow = true; + try { + isShow = mService.isControlAllowed(op, packageName); + } catch (RemoteException e) { + } + return isShow; + } + + /** @hide */ + public boolean getPrivacyGuardSettingForPackage(int uid, String packageName) { + try { + return mService.getPrivacyGuardSettingForPackage(uid, packageName); + } catch (RemoteException e) { + } + return false; + } + + /** @hide */ + public void setPrivacyGuardSettingForPackage(int uid, String packageName, + boolean state) { + try { + mService.setPrivacyGuardSettingForPackage(uid, packageName, state); + } catch (RemoteException e) { + } + } + + /** @hide */ + public void resetCounters() { + try { + mService.resetCounters(); + } catch (RemoteException e) { + } + } } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 7cae745..c829daa 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -23,6 +23,7 @@ import android.annotation.StringRes; import android.annotation.XmlRes; import android.content.ComponentName; import android.content.ContentResolver; +import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; @@ -1028,12 +1029,13 @@ final class ApplicationPackageManager extends PackageManager { if (app.packageName.equals("system")) { return mContext.mMainThread.getSystemContext().getResources(); } + final boolean sameUid = (app.uid == Process.myUid()); final Resources r = mContext.mMainThread.getTopLevelResources( sameUid ? app.sourceDir : app.publicSourceDir, sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs, app.resourceDirs, app.sharedLibraryFiles, Display.DEFAULT_DISPLAY, - null, mContext.mPackageInfo); + null, mContext.mPackageInfo, mContext, app.packageName); if (r != null) { return r; } @@ -1069,6 +1071,49 @@ final class ApplicationPackageManager extends PackageManager { throw new NameNotFoundException("Package " + appPackageName + " doesn't exist"); } + /** @hide */ + @Override public Resources getThemedResourcesForApplication( + ApplicationInfo app, String themePkgName) throws NameNotFoundException { + if (app.packageName.equals("system")) { + return mContext.mMainThread.getSystemContext().getResources(); + } + + Resources r = mContext.mMainThread.getTopLevelThemedResources( + app.uid == Process.myUid() ? app.sourceDir : app.publicSourceDir, + Display.DEFAULT_DISPLAY, null, mContext.mPackageInfo, app.packageName, + themePkgName); + if (r != null) { + return r; + } + throw new NameNotFoundException("Unable to open " + app.publicSourceDir); + } + + /** @hide */ + @Override public Resources getThemedResourcesForApplication( + String appPackageName, String themePkgName) throws NameNotFoundException { + return getThemedResourcesForApplication( + getApplicationInfo(appPackageName, 0), themePkgName); + } + + /** @hide */ + @Override + public Resources getThemedResourcesForApplicationAsUser(String appPackageName, + String themePackageName, int userId) throws NameNotFoundException { + if (userId < 0) { + throw new IllegalArgumentException( + "Call does not support special user #" + userId); + } + try { + ApplicationInfo ai = mPM.getApplicationInfo(appPackageName, 0, userId); + if (ai != null) { + return getThemedResourcesForApplication(ai, themePackageName); + } + } catch (RemoteException e) { + throw new RuntimeException("Package manager has died", e); + } + throw new NameNotFoundException("Package " + appPackageName + " doesn't exist"); + } + int mCachedSafeMode = -1; @Override public boolean isSafeMode() { try { @@ -1993,6 +2038,28 @@ final class ApplicationPackageManager extends PackageManager { } @Override + public void setComponentProtectedSetting(ComponentName componentName, boolean newState) { + try { + mPM.setComponentProtectedSetting(componentName, newState, mContext.getUserId()); + } catch (RemoteException re) { + Log.e(TAG, "Failed to set component protected setting", re); + } + } + + /** @hide */ + @Override + public boolean isComponentProtected(String callingPackage, int callingUid, + ComponentName componentName) { + try { + return mPM.isComponentProtected(callingPackage, callingUid, componentName, + mContext.getUserId()); + } catch (RemoteException re) { + Log.e(TAG, "Failed to get component protected setting", re); + return false; + } + } + + @Override public PackageInstaller getPackageInstaller() { synchronized (mLock) { if (mInstaller == null) { @@ -2240,4 +2307,30 @@ final class ApplicationPackageManager extends PackageManager { return false; } } + + /** + * @hide + */ + @Override + public void updateIconMaps(String pkgName) { + try { + mPM.updateIconMapping(pkgName); + } catch (RemoteException re) { + Log.e(TAG, "Failed to update icon maps", re); + } + } + + /** + * @hide + */ + @Override + public int processThemeResources(String themePkgName) { + try { + return mPM.processThemeResources(themePkgName); + } catch (RemoteException e) { + Log.e(TAG, "Unable to process theme resources for " + themePkgName, e); + } + + return 0; + } } diff --git a/core/java/android/app/ComposedIconInfo.aidl b/core/java/android/app/ComposedIconInfo.aidl new file mode 100644 index 0000000..8a1bab5 --- /dev/null +++ b/core/java/android/app/ComposedIconInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014 The CyanogenMod 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.app; + +/** @hide */ +parcelable ComposedIconInfo; diff --git a/core/java/android/app/ComposedIconInfo.java b/core/java/android/app/ComposedIconInfo.java new file mode 100644 index 0000000..f49c230 --- /dev/null +++ b/core/java/android/app/ComposedIconInfo.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2014 The CyanogenMod 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.app; + +import android.os.Parcel; +import android.os.Parcelable; + +/** @hide */ +public class ComposedIconInfo implements Parcelable { + public int iconUpon, iconMask; + public int[] iconBacks; + public float iconScale; + public float iconRotation; + // value used to provide some randomization to the angle of rotation + public float iconRotationVariance; + public float iconTranslationX; + public float iconTranslationY; + public int iconDensity; + public int iconSize; + public float[] colorFilter; + + // Palettized background items + public int iconPaletteBack; + public SwatchType swatchType; + public int[] defaultSwatchColors; + + public ComposedIconInfo() { + super(); + iconPaletteBack = 0; + swatchType = SwatchType.None; + iconRotation = 0; + iconTranslationX = 0; + iconTranslationY = 0; + iconScale = 1f; + } + + private ComposedIconInfo(Parcel source) { + iconScale = source.readFloat(); + iconRotation = source.readFloat(); + iconRotationVariance = source.readFloat(); + iconTranslationX = source.readFloat(); + iconTranslationY = source.readFloat(); + iconDensity = source.readInt(); + iconSize = source.readInt(); + int backCount = source.readInt(); + if (backCount > 0) { + iconBacks = new int[backCount]; + for (int i = 0; i < backCount; i++) { + iconBacks[i] = source.readInt(); + } + } + iconMask = source.readInt(); + iconUpon = source.readInt(); + int colorFilterSize = source.readInt(); + if (colorFilterSize > 0) { + colorFilter = new float[colorFilterSize]; + for (int i = 0; i < colorFilterSize; i++) { + colorFilter[i] = source.readFloat(); + } + } + iconPaletteBack = source.readInt(); + swatchType = SwatchType.values()[source.readInt()]; + int numDefaultColors = source.readInt(); + if (numDefaultColors > 0) { + defaultSwatchColors = new int[numDefaultColors]; + for (int i = 0; i < numDefaultColors; i++) { + defaultSwatchColors[i] = source.readInt(); + } + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeFloat(iconScale); + dest.writeFloat(iconRotation); + dest.writeFloat(iconRotationVariance); + dest.writeFloat(iconTranslationX); + dest.writeFloat(iconTranslationY); + dest.writeInt(iconDensity); + dest.writeInt(iconSize); + dest.writeInt(iconBacks != null ? iconBacks.length : 0); + if (iconBacks != null) { + for (int resId : iconBacks) { + dest.writeInt(resId); + } + } + dest.writeInt(iconMask); + dest.writeInt(iconUpon); + if (colorFilter != null) { + dest.writeInt(colorFilter.length); + for (float val : colorFilter) { + dest.writeFloat(val); + } + } else { + dest.writeInt(0); + } + dest.writeInt(iconPaletteBack); + dest.writeInt(swatchType.ordinal()); + if (defaultSwatchColors != null) { + dest.writeInt(defaultSwatchColors.length); + for (int color : defaultSwatchColors) { + dest.writeInt(color); + } + } else { + dest.writeInt(0); + } + } + + public static final Creator<ComposedIconInfo> CREATOR + = new Creator<ComposedIconInfo>() { + @Override + public ComposedIconInfo createFromParcel(Parcel source) { + return new ComposedIconInfo(source); + } + + @Override + public ComposedIconInfo[] newArray(int size) { + return new ComposedIconInfo[0]; + } + }; + + public enum SwatchType { + None, + Vibrant, + VibrantLight, + VibrantDark, + Muted, + MutedLight, + MutedDark + } +} diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 235f294..6896c21 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -74,6 +74,7 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; class ReceiverRestrictedContext extends ContextWrapper { ReceiverRestrictedContext(Context base) { @@ -269,6 +270,15 @@ class ContextImpl extends Context { } @Override + public void recreateTheme() { + if (mTheme != null) { + Resources.Theme newTheme = mResources.newTheme(); + newTheme.applyStyle(mThemeResource, true); + mTheme.setTo(newTheme); + } + } + + @Override public ClassLoader getClassLoader() { return mPackageInfo != null ? mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader(); @@ -1657,13 +1667,19 @@ class ContextImpl extends Context { @Override public Context createApplicationContext(ApplicationInfo application, int flags) throws NameNotFoundException { + return createApplicationContext(application, null, flags); + } + + @Override + public Context createApplicationContext(ApplicationInfo application, String themePackageName, + int flags) throws NameNotFoundException { LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(), flags | CONTEXT_REGISTER_PACKAGE); if (pi != null) { final boolean restricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED; ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken, new UserHandle(UserHandle.getUserId(application.uid)), restricted, - mDisplay, null, Display.INVALID_DISPLAY); + mDisplay, null, Display.INVALID_DISPLAY, themePackageName); if (c.mResources != null) { return c; } @@ -1676,24 +1692,30 @@ class ContextImpl extends Context { @Override public Context createPackageContext(String packageName, int flags) throws NameNotFoundException { - return createPackageContextAsUser(packageName, flags, + return createPackageContextAsUser(packageName, null, flags, mUser != null ? mUser : Process.myUserHandle()); } @Override public Context createPackageContextAsUser(String packageName, int flags, UserHandle user) throws NameNotFoundException { + return createPackageContextAsUser(packageName, null, flags, user); + } + + @Override + public Context createPackageContextAsUser(String packageName, String themePackageName, + int flags, UserHandle user) throws NameNotFoundException { final boolean restricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED; if (packageName.equals("system") || packageName.equals("android")) { return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, - user, restricted, mDisplay, null, Display.INVALID_DISPLAY); + user, restricted, mDisplay, null, Display.INVALID_DISPLAY, themePackageName); } LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(), flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier()); if (pi != null) { ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken, - user, restricted, mDisplay, null, Display.INVALID_DISPLAY); + user, restricted, mDisplay, null, Display.INVALID_DISPLAY, themePackageName); if (c.mResources != null) { return c; } @@ -1774,7 +1796,7 @@ class ContextImpl extends Context { static ContextImpl createSystemContext(ActivityThread mainThread) { LoadedApk packageInfo = new LoadedApk(mainThread); ContextImpl context = new ContextImpl(null, mainThread, - packageInfo, null, null, false, null, null, Display.INVALID_DISPLAY); + packageInfo, null, null, false, null, null, Display.INVALID_DISPLAY, null); context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(), context.mResourcesManager.getDisplayMetricsLocked()); return context; @@ -1783,19 +1805,27 @@ class ContextImpl extends Context { static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) { if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); return new ContextImpl(null, mainThread, - packageInfo, null, null, false, null, null, Display.INVALID_DISPLAY); + packageInfo, null, null, false, null, null, Display.INVALID_DISPLAY, null); } static ContextImpl createActivityContext(ActivityThread mainThread, LoadedApk packageInfo, int displayId, Configuration overrideConfiguration) { if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); return new ContextImpl(null, mainThread, packageInfo, null, null, false, - null, overrideConfiguration, displayId); + null, overrideConfiguration, displayId, null); } private ContextImpl(ContextImpl container, ActivityThread mainThread, LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted, Display display, Configuration overrideConfiguration, int createDisplayWithId) { + this(container, mainThread, packageInfo, activityToken, user, restricted, display, + overrideConfiguration, createDisplayWithId, null); + } + + private ContextImpl(ContextImpl container, ActivityThread mainThread, + LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted, + Display display, Configuration overrideConfiguration, int createDisplayWithId, + String themePackageName) { mOuterContext = this; mMainThread = mainThread; @@ -1829,16 +1859,27 @@ class ContextImpl extends Context { mDisplay = (createDisplayWithId == Display.INVALID_DISPLAY) ? display : ResourcesManager.getInstance().getAdjustedDisplay(displayId, mDisplayAdjustments); + // We need to create the content resolver before all the context resources creation because + // the content resolver is reference by the outer context while the theme information + // is created. + mContentResolver = new ApplicationContentResolver(this, mainThread, user); + Resources resources = packageInfo.getResources(mainThread); if (resources != null) { if (displayId != Display.DEFAULT_DISPLAY + || themePackageName != null || overrideConfiguration != null || (compatInfo != null && compatInfo.applicationScale != resources.getCompatibilityInfo().applicationScale)) { - resources = mResourcesManager.getTopLevelResources(packageInfo.getResDir(), - packageInfo.getSplitResDirs(), packageInfo.getOverlayDirs(), + resources = themePackageName == null ? mResourcesManager.getTopLevelResources( + packageInfo.getResDir(), packageInfo.getSplitResDirs(), + packageInfo.getOverlayDirs(), packageInfo.getApplicationInfo().sharedLibraryFiles, displayId, - overrideConfiguration, compatInfo); + packageInfo.getAppDir(), overrideConfiguration, compatInfo, mOuterContext, + packageInfo.getApplicationInfo().isThemeable) : + mResourcesManager.getTopLevelThemedResources(packageInfo.getResDir(), displayId, + packageInfo.getPackageName(), themePackageName, compatInfo, + packageInfo.getApplicationInfo().isThemeable); } } mResources = resources; @@ -1859,8 +1900,6 @@ class ContextImpl extends Context { mOpPackageName = mBasePackageName; } } - - mContentResolver = new ApplicationContentResolver(this, mainThread, user); } void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) { @@ -1950,9 +1989,12 @@ class ContextImpl extends Context { * unable to create, they are filtered by replacing with {@code null}. */ private File[] ensureDirsExistOrFilter(File[] dirs) { - File[] result = new File[dirs.length]; + ArrayList<File> result = new ArrayList<File>(dirs.length); for (int i = 0; i < dirs.length; i++) { File dir = dirs[i]; + if (Environment.MEDIA_REMOVED.equals(Environment.getStorageState(dir))) { + continue; + } if (!dir.exists()) { if (!dir.mkdirs()) { // recheck existence in case of cross-process race @@ -1974,9 +2016,14 @@ class ContextImpl extends Context { } } } - result[i] = dir; + result.add(dir); + } + + // Make sure there is at least one element, let the callers handle that + if (result.size() == 0) { + result.add(null); } - return result; + return result.toArray(new File[result.size()]); } // ---------------------------------------------------------------------- diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java index fb0e79b..682ab97 100644 --- a/core/java/android/app/DownloadManager.java +++ b/core/java/android/app/DownloadManager.java @@ -266,6 +266,13 @@ public class DownloadManager { */ public final static int PAUSED_UNKNOWN = 4; + /** + * Value of {@link #COLUMN_REASON} when the download is paused by manual. + * + * @hide + */ + public final static int PAUSED_BY_MANUAL = 5; + /** * Broadcast intent action sent by the download manager when a download completes. */ @@ -865,6 +872,7 @@ public class DownloadManager { parts.add(statusClause("=", Downloads.Impl.STATUS_WAITING_TO_RETRY)); parts.add(statusClause("=", Downloads.Impl.STATUS_WAITING_FOR_NETWORK)); parts.add(statusClause("=", Downloads.Impl.STATUS_QUEUED_FOR_WIFI)); + parts.add(statusClause("=", Downloads.Impl.STATUS_PAUSED_BY_MANUAL)); } if ((mStatusFlags & STATUS_SUCCESSFUL) != 0) { parts.add(statusClause("=", Downloads.Impl.STATUS_SUCCESS)); @@ -1100,6 +1108,34 @@ public class DownloadManager { } /** + * Pause the given running download by manual. + * + * @param id the ID of the download to be paused + * @return the number of downloads actually updated + * @hide + */ + public int pauseDownload(long id) { + ContentValues values = new ContentValues(); + values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_PAUSED_BY_MANUAL); + + return mResolver.update(ContentUris.withAppendedId(mBaseUri, id), values, null, null); + } + + /** + * Resume the given paused download by manual. + * + * @param id the ID of the download to be resumed + * @return the number of downloads actually updated + * @hide + */ + public int resumeDownload(long id) { + ContentValues values = new ContentValues(); + values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_RUNNING); + + return mResolver.update(ContentUris.withAppendedId(mBaseUri, id), values, null, null); + } + + /** * Returns maximum size, in bytes, of downloads that may go over a mobile connection; or null if * there's no limit * @@ -1335,6 +1371,9 @@ public class DownloadManager { case Downloads.Impl.STATUS_QUEUED_FOR_WIFI: return PAUSED_QUEUED_FOR_WIFI; + case Downloads.Impl.STATUS_PAUSED_BY_MANUAL: + return PAUSED_BY_MANUAL; + default: return PAUSED_UNKNOWN; } @@ -1390,6 +1429,7 @@ public class DownloadManager { case Downloads.Impl.STATUS_WAITING_TO_RETRY: case Downloads.Impl.STATUS_WAITING_FOR_NETWORK: case Downloads.Impl.STATUS_QUEUED_FOR_WIFI: + case Downloads.Impl.STATUS_PAUSED_BY_MANUAL: return STATUS_PAUSED; case Downloads.Impl.STATUS_SUCCESS: diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 5ed839e..7221e47 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -122,12 +122,14 @@ public interface IActivityManager extends IInterface { public void activitySlept(IBinder token) throws RemoteException; public void activityDestroyed(IBinder token) throws RemoteException; public String getCallingPackage(IBinder token) throws RemoteException; + public String getCallingPackageForBroadcast(boolean foreground) throws RemoteException; public ComponentName getCallingActivity(IBinder token) throws RemoteException; public List<IAppTask> getAppTasks(String callingPackage) throws RemoteException; public int addAppTask(IBinder activityToken, Intent intent, ActivityManager.TaskDescription description, Bitmap thumbnail) throws RemoteException; public Point getAppTaskThumbnailSize() throws RemoteException; public List<RunningTaskInfo> getTasks(int maxNum, int flags) throws RemoteException; + public boolean isPackageInForeground(String packageName) throws RemoteException; public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags, int userId) throws RemoteException; public ActivityManager.TaskThumbnail getTaskThumbnail(int taskId) throws RemoteException; @@ -411,7 +413,8 @@ public interface IActivityManager extends IInterface { public void keyguardWaitingForActivityDrawn() throws RemoteException; public void keyguardGoingAway(boolean disableWindowAnimations, - boolean keyguardGoingToNotificationShade) throws RemoteException; + boolean keyguardGoingToNotificationShade, + boolean keyguardShowingMedia) throws RemoteException; public boolean shouldUpRecreateTask(IBinder token, String destAffinity) throws RemoteException; @@ -839,6 +842,10 @@ public interface IActivityManager extends IInterface { int START_IN_PLACE_ANIMATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+240; int CHECK_PERMISSION_WITH_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+241; int REGISTER_TASK_STACK_LISTENER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+242; + // 243: Available + + // start of CM transactions + int GET_CALLING_PACKAGE_FOR_BROADCAST_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+244; // Start of M transactions int NOTIFY_CLEARTEXT_NETWORK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+280; @@ -864,4 +871,5 @@ public interface IActivityManager extends IInterface { = IBinder.FIRST_CALL_TRANSACTION+299; int SHOW_ASSIST_FROM_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+300; int IS_ROOT_VOICE_INTERACTION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+301; + int IS_PACKAGE_IN_FOREGROUND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+302; } diff --git a/core/java/android/app/IAlarmManager.aidl b/core/java/android/app/IAlarmManager.aidl index 327c00b..4fdbfaa 100644 --- a/core/java/android/app/IAlarmManager.aidl +++ b/core/java/android/app/IAlarmManager.aidl @@ -35,6 +35,8 @@ interface IAlarmManager { void remove(in PendingIntent operation); long getNextWakeFromIdleTime(); AlarmManager.AlarmClockInfo getNextAlarmClock(int userId); + // update the uids being synchronized by network socket request manager + void updateBlockedUids(int uid, boolean isBlocked); } diff --git a/core/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl b/core/java/android/app/IBatteryService.aidl index 96c59e2..196159b 100755..100644 --- a/core/java/android/bluetooth/IBluetoothProfileServiceConnection.aidl +++ b/core/java/android/app/IBatteryService.aidl @@ -1,11 +1,11 @@ -/* - * Copyright (C) 2014 The Android Open Source Project +/** + * Copyright (c) 2016, The CyanogenMod 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 + * 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, @@ -14,17 +14,13 @@ * limitations under the License. */ -package android.bluetooth; - -import android.content.ComponentName; -import android.os.IBinder; +package android.app; /** - * Callback for bluetooth profile connections. + * System private API for talking with the battery service. * * {@hide} */ -interface IBluetoothProfileServiceConnection { - void onServiceConnected(in ComponentName comp, in IBinder service); - void onServiceDisconnected(in ComponentName comp); +interface IBatteryService { + boolean isDockBatterySupported(); } diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index f78fb47..e749d0a 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -55,6 +55,9 @@ interface INotificationManager void setPackageVisibilityOverride(String pkg, int uid, int visibility); int getPackageVisibilityOverride(String pkg, int uid); + void setShowNotificationForPackageOnKeyguard(String pkg, int uid, int status); + int getShowNotificationForPackageOnKeyguard(String pkg, int uid); + // TODO: Remove this when callers have been migrated to the equivalent // INotificationListener method. StatusBarNotification[] getActiveNotifications(String callingPkg); diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl index ccba250..c717459 100644 --- a/core/java/android/app/IWallpaperManager.aidl +++ b/core/java/android/app/IWallpaperManager.aidl @@ -30,6 +30,12 @@ interface IWallpaperManager { * Set the wallpaper. */ ParcelFileDescriptor setWallpaper(String name, in String callingPackage); + + /** + * Set the keyguard wallpaper. + * @hide + */ + ParcelFileDescriptor setKeyguardWallpaper(String name, in String callingPackage); /** * Set the live wallpaper. @@ -46,6 +52,13 @@ interface IWallpaperManager { */ ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb, out Bundle outParams); + + /** + * Get the keyguard wallpaper. + * @hide + */ + ParcelFileDescriptor getKeyguardWallpaper(IWallpaperManagerCallback cb, + out Bundle outParams); /** * Get information about a live wallpaper. @@ -57,6 +70,12 @@ interface IWallpaperManager { */ void clearWallpaper(in String callingPackage); + /* + * Clear the keyguard wallpaper. + * @hide + */ + void clearKeyguardWallpaper(); + /** * Return whether there is a wallpaper set with the given name. */ diff --git a/core/java/android/app/IWallpaperManagerCallback.aidl b/core/java/android/app/IWallpaperManagerCallback.aidl index 991b2bc..b217318 100644 --- a/core/java/android/app/IWallpaperManagerCallback.aidl +++ b/core/java/android/app/IWallpaperManagerCallback.aidl @@ -28,4 +28,9 @@ oneway interface IWallpaperManagerCallback { * Called when the wallpaper has changed */ void onWallpaperChanged(); + + /** + * Called when the keygaurd wallpaper has changed + */ + void onKeyguardWallpaperChanged(); } diff --git a/core/java/android/app/IconPackHelper.java b/core/java/android/app/IconPackHelper.java new file mode 100644 index 0000000..9c71ddd --- /dev/null +++ b/core/java/android/app/IconPackHelper.java @@ -0,0 +1,1071 @@ +/* + * Copyright (C) 2014 The CyanogenMod 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.app; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import android.content.pm.PackageInfo; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.Paint; +import android.graphics.PaintFlagsDrawFilter; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.PaintDrawable; +import android.graphics.drawable.VectorDrawable; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; +import android.util.TypedValue; + +import com.android.internal.util.cm.palette.Palette; + +import org.cyanogenmod.internal.themes.IIconCacheManager; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ThemeUtils; +import android.content.res.AssetManager; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.XmlResourceParser; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.util.DisplayMetrics; + +/** @hide */ +public class IconPackHelper { + private static final String TAG = IconPackHelper.class.getSimpleName(); + + private static final boolean DEBUG = false; + + private static final String ICON_MASK_TAG = "iconmask"; + private static final String ICON_BACK_TAG = "iconback"; + private static final String ICON_UPON_TAG = "iconupon"; + private static final String ICON_SCALE_TAG = "scale"; + private static final String ICON_ROTATE_TAG = "rotate"; + private static final String ICON_TRANSLATE_TAG = "translate"; + private static final String ICON_BACK_FORMAT = "iconback%d"; + + // Palettized icon background constants + private static final String ICON_PALETTIZED_BACK_TAG = "paletteback"; + private static final String IMG_ATTR = "img"; + private static final String SWATCH_TYPE_ATTR = "swatchType"; + private static final String DEFAULT_SWATCH_COLOR_ATTR = "defaultSwatchColor"; + private static final String VIBRANT_VALUE = "vibrant"; + private static final String VIBRANT_LIGHT_VALUE = "vibrantLight"; + private static final String VIBRANT_DARK_VALUE = "vibrantDark"; + private static final String MUTED_VALUE = "muted"; + private static final String MUTED_LIGHT_VALUE = "mutedLight"; + private static final String MUTED_DARK_VALUE = "mutedDark"; + private static final int NUM_PALETTE_COLORS = 32; + + // Rotation and translation constants + private static final String ANGLE_ATTR = "angle"; + private static final String ANGLE_VARIANCE = "plusMinus"; + private static final String TRANSLATE_X_ATTR = "xOffset"; + private static final String TRANSLATE_Y_ATTR = "yOffset"; + + private static final ComponentName ICON_BACK_COMPONENT; + private static final ComponentName ICON_MASK_COMPONENT; + private static final ComponentName ICON_UPON_COMPONENT; + private static final ComponentName ICON_SCALE_COMPONENT; + + private static final float DEFAULT_SCALE = 1.0f; + private static final int COMPOSED_ICON_COOKIE = 128; + + private static final String ICON_CACHE_SERVICE = "cmiconcache"; + + public static final String SYSTEM_THEME_PATH = "/data/system/theme"; + public static final String SYSTEM_THEME_ICON_CACHE_DIR = SYSTEM_THEME_PATH + + File.separator + "icons"; + + private final Context mContext; + private Map<ComponentName, String> mIconPackResourceMap; + private String mLoadedIconPackName; + private Resources mLoadedIconPackResource; + private ComposedIconInfo mComposedIconInfo; + private int mIconBackCount = 0; + private ColorFilterUtils.Builder mFilterBuilder; + + static { + ICON_BACK_COMPONENT = new ComponentName(ICON_BACK_TAG, ""); + ICON_MASK_COMPONENT = new ComponentName(ICON_MASK_TAG, ""); + ICON_UPON_COMPONENT = new ComponentName(ICON_UPON_TAG, ""); + ICON_SCALE_COMPONENT = new ComponentName(ICON_SCALE_TAG, ""); + } + + public IconPackHelper(Context context) { + mContext = context; + mIconPackResourceMap = new HashMap<ComponentName, String>(); + ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + mComposedIconInfo = new ComposedIconInfo(); + mComposedIconInfo.iconSize = am.getLauncherLargeIconSize(); + mComposedIconInfo.iconDensity = am.getLauncherLargeIconDensity(); + mFilterBuilder = new ColorFilterUtils.Builder(); + } + + private void loadResourcesFromXmlParser(XmlPullParser parser, + Map<ComponentName, String> iconPackResources) + throws XmlPullParserException, IOException { + mIconBackCount = 0; + int eventType = parser.getEventType(); + do { + + if (eventType != XmlPullParser.START_TAG) { + continue; + } + + if (parseComposedIconComponent(parser, iconPackResources)) { + continue; + } + + if (ColorFilterUtils.parseIconFilter(parser, mFilterBuilder)) { + continue; + } + + if (parser.getName().equalsIgnoreCase(ICON_SCALE_TAG)) { + String factor = parser.getAttributeValue(null, "factor"); + if (factor == null) { + if (parser.getAttributeCount() == 1) { + factor = parser.getAttributeValue(0); + } + } + iconPackResources.put(ICON_SCALE_COMPONENT, factor); + continue; + } + + if (parseRotationComponent(parser, mComposedIconInfo)) { + continue; + } + + if (parseTranslationComponent(parser, mComposedIconInfo)) { + continue; + } + + if (!parser.getName().equalsIgnoreCase("item")) { + continue; + } + + String component = parser.getAttributeValue(null, "component"); + String drawable = parser.getAttributeValue(null, "drawable"); + + // Validate component/drawable exist + if (TextUtils.isEmpty(component) || TextUtils.isEmpty(drawable)) { + continue; + } + + // Validate format/length of component + if (!component.startsWith("ComponentInfo{") || !component.endsWith("}") + || component.length() < 16 || drawable.length() == 0) { + continue; + } + + // Sanitize stored value + component = component.substring(14, component.length() - 1).toLowerCase(); + + ComponentName name = null; + if (!component.contains("/")) { + // Package icon reference + name = new ComponentName(component.toLowerCase(), ""); + } else { + name = ComponentName.unflattenFromString(component); + } + + if (name != null) { + iconPackResources.put(name, drawable); + } + } while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT); + } + + private boolean isComposedIconComponent(String tag) { + return ICON_MASK_TAG.equalsIgnoreCase(tag) || + ICON_BACK_TAG.equalsIgnoreCase(tag) || + ICON_UPON_TAG.equalsIgnoreCase(tag) || + ICON_PALETTIZED_BACK_TAG.equalsIgnoreCase(tag); + } + + private boolean parseComposedIconComponent(XmlPullParser parser, + Map<ComponentName, String> iconPackResources) { + String icon; + String tag = parser.getName(); + if (!isComposedIconComponent(tag)) { + return false; + } + + if (parser.getAttributeCount() >= 1) { + if (ICON_BACK_TAG.equalsIgnoreCase(tag)) { + mIconBackCount = parser.getAttributeCount(); + for (int i = 0; i < mIconBackCount; i++) { + tag = String.format(ICON_BACK_FORMAT, i); + icon = parser.getAttributeValue(i); + iconPackResources.put(new ComponentName(tag, ""), icon); + } + } else if (ICON_PALETTIZED_BACK_TAG.equalsIgnoreCase(tag)) { + parsePalettizedBackground(parser, mComposedIconInfo); + } else { + icon = parser.getAttributeValue(0); + iconPackResources.put(new ComponentName(tag, ""), + icon); + } + return true; + } + + return false; + } + + private void parsePalettizedBackground(XmlPullParser parser, ComposedIconInfo iconInfo) { + int attrCount = parser.getAttributeCount(); + ArrayList<Integer> convertedColors = new ArrayList<Integer>(); + for (int i = 0; i < attrCount; i++) { + String name = parser.getAttributeName(i); + String value = parser.getAttributeValue(i); + if (TextUtils.isEmpty(name)) { + Log.w(TAG, "Attribute name cannot be empty or null"); + continue; + } + if (TextUtils.isEmpty(value)) { + Log.w(TAG, "Attribute value cannot be empty or null"); + continue; + } + if (IMG_ATTR.equalsIgnoreCase(name)) { + iconInfo.iconPaletteBack = getResourceIdForDrawable(value); + if (DEBUG) { + Log.d(TAG, String.format("img=%s, resId=%d", value, + iconInfo.iconPaletteBack)); + } + } else if (SWATCH_TYPE_ATTR.equalsIgnoreCase(name)) { + ComposedIconInfo.SwatchType type = ComposedIconInfo.SwatchType.None; + if (VIBRANT_VALUE.equalsIgnoreCase(value)) { + type = ComposedIconInfo.SwatchType.Vibrant; + } else if (VIBRANT_LIGHT_VALUE.equalsIgnoreCase(value)) { + type = ComposedIconInfo.SwatchType.VibrantLight; + } else if (VIBRANT_DARK_VALUE.equalsIgnoreCase(value)) { + type = ComposedIconInfo.SwatchType.VibrantDark; + } else if (MUTED_VALUE.equalsIgnoreCase(value)) { + type = ComposedIconInfo.SwatchType.Muted; + } else if (MUTED_LIGHT_VALUE.equalsIgnoreCase(value)) { + type = ComposedIconInfo.SwatchType.MutedLight; + } else if (MUTED_DARK_VALUE.equalsIgnoreCase(value)) { + type = ComposedIconInfo.SwatchType.MutedDark; + } + if (type != ComposedIconInfo.SwatchType.None) { + iconInfo.swatchType = type; + if (DEBUG) Log.d(TAG, "PaletteType=" + type); + } + } else if (name.startsWith(DEFAULT_SWATCH_COLOR_ATTR)) { + try { + // ensure alpha is always 0xff + convertedColors.add(Color.parseColor(value) | 0xff000000); + } catch (IllegalArgumentException e) { + Log.w(TAG, "Invalid color format", e); + } + } + if (convertedColors.size() > 0) { + iconInfo.defaultSwatchColors = new int[convertedColors.size()]; + for (int j = 0; j < convertedColors.size(); j++) { + iconInfo.defaultSwatchColors[j] = convertedColors.get(j); + } + } + } + } + + private boolean parseRotationComponent(XmlPullParser parser, ComposedIconInfo iconInfo) { + if (!parser.getName().equalsIgnoreCase(ICON_ROTATE_TAG)) return false; + String angle = parser.getAttributeValue(null, ANGLE_ATTR); + String variance = parser.getAttributeValue(null, ANGLE_VARIANCE); + if (angle != null) { + try { + iconInfo.iconRotation = Float.valueOf(angle); + } catch (NumberFormatException e) { + Log.w(TAG, "Error parsing " + ANGLE_ATTR, e); + } + } + if (variance != null) { + try { + iconInfo.iconRotationVariance = Float.valueOf(variance); + } catch (NumberFormatException e) { + Log.w(TAG, "Error parsing " + ANGLE_VARIANCE, e); + } + } + return true; + } + + private boolean parseTranslationComponent(XmlPullParser parser, ComposedIconInfo iconInfo) { + if (!parser.getName().equalsIgnoreCase(ICON_TRANSLATE_TAG)) return false; + + final float density = mContext.getResources().getDisplayMetrics().density; + String translateX = parser.getAttributeValue(null, TRANSLATE_X_ATTR); + String translateY = parser.getAttributeValue(null, TRANSLATE_Y_ATTR); + if (translateX != null) { + try { + iconInfo.iconTranslationX = Float.valueOf(translateX) * density; + } catch (NumberFormatException e) { + Log.w(TAG, "Error parsing " + TRANSLATE_X_ATTR, e); + } + } + if (translateY != null) { + try { + iconInfo.iconTranslationY = Float.valueOf(translateY) * density; + } catch (NumberFormatException e) { + Log.w(TAG, "Error parsing " + TRANSLATE_Y_ATTR, e); + } + } + return true; + } + + public void loadIconPack(String packageName) throws NameNotFoundException { + if (packageName == null) { + mLoadedIconPackResource = null; + mLoadedIconPackName = null; + mComposedIconInfo.iconBacks = null; + mComposedIconInfo.iconMask = mComposedIconInfo.iconUpon = 0; + mComposedIconInfo.iconScale = 0; + mComposedIconInfo.iconRotation = 0; + mComposedIconInfo.iconTranslationX = 0; + mComposedIconInfo.iconTranslationY = 0; + mComposedIconInfo.colorFilter = null; + mComposedIconInfo.iconPaletteBack = 0; + mComposedIconInfo.swatchType = ComposedIconInfo.SwatchType.None; + } else { + mIconBackCount = 0; + Resources res = createIconResource(mContext, packageName); + mLoadedIconPackResource = res; + mLoadedIconPackName = packageName; + mIconPackResourceMap = getIconResMapFromXml(res, packageName); + loadComposedIconComponents(); + ColorMatrix cm = mFilterBuilder.build(); + if (cm != null) { + mComposedIconInfo.colorFilter = cm.getArray().clone(); + } + } + } + + public ComposedIconInfo getComposedIconInfo() { + return mComposedIconInfo; + } + + private void loadComposedIconComponents() { + mComposedIconInfo.iconMask = getResourceIdForName(ICON_MASK_COMPONENT); + mComposedIconInfo.iconUpon = getResourceIdForName(ICON_UPON_COMPONENT); + + // Take care of loading iconback which can have multiple images + if (mIconBackCount > 0) { + mComposedIconInfo.iconBacks = new int[mIconBackCount]; + for (int i = 0; i < mIconBackCount; i++) { + mComposedIconInfo.iconBacks[i] = + getResourceIdForName( + new ComponentName(String.format(ICON_BACK_FORMAT, i), "")); + } + } + + // Get the icon scale from this pack + String scale = mIconPackResourceMap.get(ICON_SCALE_COMPONENT); + if (scale != null) { + try { + mComposedIconInfo.iconScale = Float.valueOf(scale); + } catch (NumberFormatException e) { + mComposedIconInfo.iconScale = DEFAULT_SCALE; + } + } else { + mComposedIconInfo.iconScale = DEFAULT_SCALE; + } + } + + private int getResourceIdForName(ComponentName component) { + String item = mIconPackResourceMap.get(component); + if (!TextUtils.isEmpty(item)) { + return getResourceIdForDrawable(item); + } + return 0; + } + + public static Resources createIconResource(Context context, String packageName) + throws NameNotFoundException { + PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); + String themeApk = info.applicationInfo.publicSourceDir; + + String prefixPath; + String iconApkPath; + if (info.isLegacyIconPackApk) { + iconApkPath = ""; + prefixPath = ""; + } else { + prefixPath = ThemeUtils.ICONS_PATH; //path inside APK + iconApkPath = ThemeUtils.getIconPackApkPath(packageName); + } + + AssetManager assets = new AssetManager(); + assets.addIconPath(themeApk, iconApkPath, + prefixPath, Resources.THEME_ICON_PKG_ID); + + DisplayMetrics dm = context.getResources().getDisplayMetrics(); + Configuration config = context.getResources().getConfiguration(); + Resources res = new Resources(assets, dm, config); + return res; + } + + public Map<ComponentName, String> getIconResMapFromXml(Resources res, String packageName) { + XmlPullParser parser = null; + InputStream inputStream = null; + Map<ComponentName, String> iconPackResources = new HashMap<ComponentName, String>(); + + try { + inputStream = res.getAssets().open("appfilter.xml"); + XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + parser = factory.newPullParser(); + parser.setInput(inputStream, "UTF-8"); + } catch (Exception e) { + // Catch any exception since we want to fall back to parsing the xml/ + // resource in all cases + int resId = res.getIdentifier("appfilter", "xml", packageName); + if (resId != 0) { + parser = res.getXml(resId); + } + } + + if (parser != null) { + try { + loadResourcesFromXmlParser(parser, iconPackResources); + return iconPackResources; + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + // Cleanup resources + if (parser instanceof XmlResourceParser) { + ((XmlResourceParser) parser).close(); + } else if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + } + } + } + } + + // Application uses a different theme format (most likely launcher pro) + int arrayId = res.getIdentifier("theme_iconpack", "array", packageName); + if (arrayId == 0) { + arrayId = res.getIdentifier("icon_pack", "array", packageName); + } + + if (arrayId != 0) { + String[] iconPack = res.getStringArray(arrayId); + ComponentName compName = null; + for (String entry : iconPack) { + + if (TextUtils.isEmpty(entry)) { + continue; + } + + String icon = entry; + entry = entry.replaceAll("_", "."); + + compName = new ComponentName(entry.toLowerCase(), ""); + iconPackResources.put(compName, icon); + + int activityIndex = entry.lastIndexOf("."); + if (activityIndex <= 0 || activityIndex == entry.length() - 1) { + continue; + } + + String iconPackage = entry.substring(0, activityIndex); + if (TextUtils.isEmpty(iconPackage)) { + continue; + } + + String iconActivity = entry.substring(activityIndex + 1); + if (TextUtils.isEmpty(iconActivity)) { + continue; + } + + // Store entries as lower case to ensure match + iconPackage = iconPackage.toLowerCase(); + iconActivity = iconActivity.toLowerCase(); + + iconActivity = iconPackage + "." + iconActivity; + compName = new ComponentName(iconPackage, iconActivity); + iconPackResources.put(compName, icon); + } + } + return iconPackResources; + } + + boolean isIconPackLoaded() { + return mLoadedIconPackResource != null && + mLoadedIconPackName != null && + mIconPackResourceMap != null; + } + + private int getResourceIdForDrawable(String resource) { + int resId = + mLoadedIconPackResource.getIdentifier(resource, "drawable",mLoadedIconPackName); + return resId; + } + + public int getResourceIdForActivityIcon(ActivityInfo info) { + if (!isIconPackLoaded()) { + return 0; + } + ComponentName compName = new ComponentName(info.packageName.toLowerCase(), + info.name.toLowerCase()); + String drawable = mIconPackResourceMap.get(compName); + if (drawable != null) { + int resId = getResourceIdForDrawable(drawable); + if (resId != 0) return resId; + } + + // Icon pack doesn't have an icon for the activity, fallback to package icon + compName = new ComponentName(info.packageName.toLowerCase(), ""); + drawable = mIconPackResourceMap.get(compName); + if (drawable == null) { + return 0; + } + return getResourceIdForDrawable(drawable); + } + + public int getResourceIdForApp(String pkgName) { + ActivityInfo info = new ActivityInfo(); + info.packageName = pkgName; + info.name = ""; + return getResourceIdForActivityIcon(info); + } + + public Drawable getDrawableForActivity(ActivityInfo info) { + int id = getResourceIdForActivityIcon(info); + if (id == 0) return null; + return mLoadedIconPackResource.getDrawable(id, null, false); + } + + public Drawable getDrawableForActivityWithDensity(ActivityInfo info, int density) { + int id = getResourceIdForActivityIcon(info); + if (id == 0) return null; + return mLoadedIconPackResource.getDrawableForDensity(id, density, null, false); + } + + public static boolean shouldComposeIcon(ComposedIconInfo iconInfo) { + return iconInfo != null && + (iconInfo.iconBacks != null || + iconInfo.iconMask != 0 || + iconInfo.iconUpon != 0 || + iconInfo.colorFilter != null || + iconInfo.iconPaletteBack != 0 || + iconInfo.iconRotation != 0 || + iconInfo.iconRotationVariance != 0 || + iconInfo.iconTranslationX != 0 || + iconInfo.iconTranslationY != 0 || + iconInfo.iconScale != 1f); + } + + public static class IconCustomizer { + private static final Random sRandom = new Random(); + private static final IIconCacheManager sIconCacheManager; + + static { + sIconCacheManager = IIconCacheManager.Stub.asInterface( + ServiceManager.getService(ICON_CACHE_SERVICE)); + } + + public static Drawable getComposedIconDrawable(Drawable icon, Context context, + ComposedIconInfo iconInfo) { + final Resources res = context.getResources(); + return getComposedIconDrawable(icon, res, iconInfo); + } + + public static Drawable getComposedIconDrawable(Drawable icon, Resources res, + ComposedIconInfo iconInfo) { + if (iconInfo == null) return icon; + int back = 0; + int defaultSwatchColor = 0; + if (iconInfo.swatchType != ComposedIconInfo.SwatchType.None) { + back = iconInfo.iconPaletteBack; + if (iconInfo.defaultSwatchColors.length > 0) { + defaultSwatchColor = iconInfo.defaultSwatchColors[ + sRandom.nextInt(iconInfo.defaultSwatchColors.length)]; + } + } else if (iconInfo.iconBacks != null && iconInfo.iconBacks.length > 0) { + back = iconInfo.iconBacks[sRandom.nextInt(iconInfo.iconBacks.length)]; + } + Bitmap bmp = createIconBitmap(icon, res, back, defaultSwatchColor, iconInfo); + return bmp != null ? new BitmapDrawable(res, bmp): null; + } + + public static void getValue(Resources res, int resId, TypedValue outValue, + Drawable baseIcon) { + final String pkgName = res.getAssets().getAppName(); + final ComposedIconInfo iconInfo = res.getComposedIconInfo(); + if (iconInfo == null) { + // No composed icon info available so return, keeping original value + return; + } + TypedValue tempValue = new TypedValue(); + tempValue.setTo(outValue); + // Catch all exceptions and restore outValue to tempValue if one occurs + try { + outValue.assetCookie = COMPOSED_ICON_COOKIE; + outValue.data = resId & (COMPOSED_ICON_COOKIE << 24 | 0x00ffffff); + outValue.string = getCachedIconPath(pkgName, resId, outValue.density); + int hashCode = outValue.string.hashCode() & 0x7fffffff; + int defaultSwatchColor = 0; + + if (!(new File(outValue.string.toString()).exists())) { + // compose the icon and cache it + int back = 0; + if (iconInfo.swatchType != ComposedIconInfo.SwatchType.None) { + back = iconInfo.iconPaletteBack; + if (iconInfo.defaultSwatchColors.length > 0) { + defaultSwatchColor = iconInfo.defaultSwatchColors[ + hashCode % iconInfo.defaultSwatchColors.length]; + } + } else if (iconInfo.iconBacks != null && iconInfo.iconBacks.length > 0) { + back = iconInfo.iconBacks[hashCode % iconInfo.iconBacks.length]; + } + if (DEBUG) { + Log.d(TAG, "Composing icon for " + pkgName); + } + Bitmap bmp = createIconBitmap(baseIcon, res, back, defaultSwatchColor, + iconInfo); + if (!cacheComposedIcon(bmp, + getCachedIconName(pkgName, resId, outValue.density))) { + Log.w(TAG, "Unable to cache icon " + outValue.string); + // restore the original TypedValue + outValue.setTo(tempValue); + } + } + } catch (Exception e) { + // catch all, restore the original value and log it + outValue.setTo(tempValue); + Log.w(TAG, "getValue failed for " + outValue.string, e); + } + } + + private static Bitmap createIconBitmap(Drawable icon, Resources res, int iconBack, + int defaultSwatchColor, ComposedIconInfo iconInfo) { + if (iconInfo.iconSize <= 0) return null; + + final Canvas canvas = new Canvas(); + canvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.ANTI_ALIAS_FLAG, + Paint.FILTER_BITMAP_FLAG)); + + int width = 0, height = 0; + int backTintColor = 0; + if (icon instanceof PaintDrawable) { + PaintDrawable painter = (PaintDrawable) icon; + painter.setIntrinsicWidth(iconInfo.iconSize); + painter.setIntrinsicHeight(iconInfo.iconSize); + + // A PaintDrawable does not have an exact size + width = iconInfo.iconSize; + height = iconInfo.iconSize; + } else if (icon instanceof BitmapDrawable) { + // Ensure the bitmap has a density. + BitmapDrawable bitmapDrawable = (BitmapDrawable) icon; + Bitmap bitmap = bitmapDrawable.getBitmap(); + if (bitmap.getDensity() == Bitmap.DENSITY_NONE) { + bitmapDrawable.setTargetDensity(res.getDisplayMetrics()); + } + canvas.setDensity(bitmap.getDensity()); + + // If the original size of the icon isn't greater + // than twice the size of recommended large icons + // respect the original size of the icon + // otherwise enormous icons can easily create + // OOM situations. + if ((bitmap.getWidth() < (iconInfo.iconSize * 2)) + && (bitmap.getHeight() < (iconInfo.iconSize * 2))) { + width = bitmap.getWidth(); + height = bitmap.getHeight(); + } else { + width = iconInfo.iconSize; + height = iconInfo.iconSize; + } + if (iconInfo.swatchType != ComposedIconInfo.SwatchType.None) { + Palette palette = Palette.generate(bitmap, NUM_PALETTE_COLORS); + switch (iconInfo.swatchType) { + case Vibrant: + backTintColor = palette.getVibrantColor(defaultSwatchColor); + break; + case VibrantLight: + backTintColor = palette.getLightVibrantColor(defaultSwatchColor); + break; + case VibrantDark: + backTintColor = palette.getDarkVibrantColor(defaultSwatchColor); + break; + case Muted: + backTintColor = palette.getMutedColor(defaultSwatchColor); + break; + case MutedLight: + backTintColor = palette.getLightMutedColor(defaultSwatchColor); + break; + case MutedDark: + backTintColor = palette.getDarkMutedColor(defaultSwatchColor); + break; + } + if (DEBUG) { + Log.d(TAG, String.format("palette tint color=0x%08x", backTintColor)); + } + } + } else if (icon instanceof VectorDrawable) { + width = height = iconInfo.iconSize; + } + + if (width <= 0 || height <= 0) return null; + + Bitmap bitmap = Bitmap.createBitmap(width, height, + Bitmap.Config.ARGB_8888); + canvas.setBitmap(bitmap); + + // Scale the original + Rect oldBounds = new Rect(); + oldBounds.set(icon.getBounds()); + icon.setBounds(0, 0, width, height); + canvas.save(); + final float halfWidth = width / 2f; + final float halfHeight = height / 2f; + float angle = iconInfo.iconRotation; + if (iconInfo.iconRotationVariance != 0) { + angle += (sRandom.nextFloat() * (iconInfo.iconRotationVariance * 2)) + - iconInfo.iconRotationVariance; + } + canvas.scale(iconInfo.iconScale, iconInfo.iconScale, halfWidth, halfHeight); + canvas.translate(iconInfo.iconTranslationX, iconInfo.iconTranslationY); + canvas.rotate(angle, halfWidth, halfHeight); + if (iconInfo.colorFilter != null) { + Paint p = null; + if (icon instanceof BitmapDrawable) { + p = ((BitmapDrawable) icon).getPaint(); + } else if (icon instanceof PaintDrawable) { + p = ((PaintDrawable) icon).getPaint(); + } + if (p != null) p.setColorFilter(new ColorMatrixColorFilter(iconInfo.colorFilter)); + } + icon.draw(canvas); + canvas.restore(); + + // Mask off the original if iconMask is not null + if (iconInfo.iconMask != 0) { + Drawable mask = res.getDrawable(iconInfo.iconMask); + if (mask != null) { + mask.setBounds(icon.getBounds()); + ((BitmapDrawable) mask).getPaint().setXfermode( + new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + mask.draw(canvas); + } + } + // Draw the iconBacks if not null and then the original (scaled and masked) icon on top + if (iconBack != 0) { + Drawable back = res.getDrawable(iconBack); + if (back != null) { + back.setBounds(icon.getBounds()); + Paint paint = ((BitmapDrawable) back).getPaint(); + paint.setXfermode( + new PorterDuffXfermode(PorterDuff.Mode.DST_OVER)); + if (backTintColor != 0) { + paint.setColorFilter(new PorterDuffColorFilter(backTintColor, + PorterDuff.Mode.MULTIPLY)); + } + back.draw(canvas); + } + } + // Finally draw the foreground if one was supplied + if (iconInfo.iconUpon != 0) { + Drawable upon = res.getDrawable(iconInfo.iconUpon); + if (upon != null) { + upon.setBounds(icon.getBounds()); + upon.draw(canvas); + } + } + icon.setBounds(oldBounds); + bitmap.setDensity(canvas.getDensity()); + + return bitmap; + } + + private static boolean cacheComposedIcon(Bitmap bmp, String path) { + try { + return sIconCacheManager.cacheComposedIcon(bmp, path); + } catch (RemoteException e) { + Log.e(TAG, "Unable to cache icon.", e); + } + + return false; + } + + private static String getCachedIconPath(String pkgName, int resId, int density) { + return String.format("%s/%s", SYSTEM_THEME_ICON_CACHE_DIR, + getCachedIconName(pkgName, resId, density)); + } + + private static String getCachedIconName(String pkgName, int resId, int density) { + return String.format("%s_%08x_%d.png", pkgName, resId, density); + } + } + + public static class ColorFilterUtils { + private static final String TAG_FILTER = "filter"; + private static final String FILTER_HUE = "hue"; + private static final String FILTER_SATURATION = "saturation"; + private static final String FILTER_INVERT = "invert"; + private static final String FILTER_BRIGHTNESS = "brightness"; + private static final String FILTER_CONTRAST = "contrast"; + private static final String FILTER_ALPHA = "alpha"; + private static final String FILTER_TINT = "tint"; + + private static final int MIN_HUE = -180; + private static final int MAX_HUE = 180; + private static final int MIN_SATURATION = 0; + private static final int MAX_SATURATION = 200; + private static final int MIN_BRIGHTNESS = 0; + private static final int MAX_BRIGHTNESS = 200; + private static final int MIN_CONTRAST = -100; + private static final int MAX_CONTRAST = 100; + private static final int MIN_ALPHA = 0; + private static final int MAX_ALPHA = 100; + + public static boolean parseIconFilter(XmlPullParser parser, Builder builder) + throws IOException, XmlPullParserException { + String tag = parser.getName(); + if (!TAG_FILTER.equals(tag)) return false; + + int attrCount = parser.getAttributeCount(); + String attrName; + String attr = null; + int intValue; + while (attrCount-- > 0) { + attrName = parser.getAttributeName(attrCount); + if (attrName.equals("name")) { + attr = parser.getAttributeValue(attrCount); + } + } + String content = parser.nextText(); + if (attr != null && content != null && content.length() > 0) { + content = content.trim(); + if (FILTER_HUE.equalsIgnoreCase(attr)) { + intValue = clampValue(getInt(content, 0),MIN_HUE, MAX_HUE); + builder.hue(intValue); + } else if (FILTER_SATURATION.equalsIgnoreCase(attr)) { + intValue = clampValue(getInt(content, 100), + MIN_SATURATION, MAX_SATURATION); + builder.saturate(intValue); + } else if (FILTER_INVERT.equalsIgnoreCase(attr)) { + if ("true".equalsIgnoreCase(content)) { + builder.invertColors(); + } + } else if (FILTER_BRIGHTNESS.equalsIgnoreCase(attr)) { + intValue = clampValue(getInt(content, 100), + MIN_BRIGHTNESS, MAX_BRIGHTNESS); + builder.brightness(intValue); + } else if (FILTER_CONTRAST.equalsIgnoreCase(attr)) { + intValue = clampValue(getInt(content, 0), + MIN_CONTRAST, MAX_CONTRAST); + builder.contrast(intValue); + } else if (FILTER_ALPHA.equalsIgnoreCase(attr)) { + intValue = clampValue(getInt(content, 100), MIN_ALPHA, MAX_ALPHA); + builder.alpha(intValue); + } else if (FILTER_TINT.equalsIgnoreCase(attr)) { + try { + intValue = Color.parseColor(content); + builder.tint(intValue); + } catch (IllegalArgumentException e) { + Log.w(TAG, "Cannot apply tint, invalid argument: " + content); + } + } + } + return true; + } + + private static int getInt(String value, int defaultValue) { + try { + return Integer.valueOf(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + private static int clampValue(int value, int min, int max) { + return Math.min(max, Math.max(min, value)); + } + + /** + * See the following links for reference + * http://groups.google.com/group/android-developers/browse_thread/thread/9e215c83c3819953 + * http://gskinner.com/blog/archives/2007/12/colormatrix_cla.html + * @param value + */ + public static ColorMatrix adjustHue(float value) { + ColorMatrix cm = new ColorMatrix(); + value = value / 180 * (float) Math.PI; + if (value != 0) { + float cosVal = (float) Math.cos(value); + float sinVal = (float) Math.sin(value); + float lumR = 0.213f; + float lumG = 0.715f; + float lumB = 0.072f; + float[] mat = new float[]{ + lumR + cosVal * (1 - lumR) + sinVal * (-lumR), + lumG + cosVal * (-lumG) + sinVal * (-lumG), + lumB + cosVal * (-lumB) + sinVal * (1 - lumB), 0, 0, + lumR + cosVal * (-lumR) + sinVal * (0.143f), + lumG + cosVal * (1 - lumG) + sinVal * (0.140f), + lumB + cosVal * (-lumB) + sinVal * (-0.283f), 0, 0, + lumR + cosVal * (-lumR) + sinVal * (-(1 - lumR)), + lumG + cosVal * (-lumG) + sinVal * (lumG), + lumB + cosVal * (1 - lumB) + sinVal * (lumB), 0, 0, + 0, 0, 0, 1, 0, + 0, 0, 0, 0, 1}; + cm.set(mat); + } + return cm; + } + + public static ColorMatrix adjustSaturation(float saturation) { + saturation = saturation / 100; + ColorMatrix cm = new ColorMatrix(); + cm.setSaturation(saturation); + + return cm; + } + + public static ColorMatrix invertColors() { + float[] matrix = { + -1, 0, 0, 0, 255, //red + 0, -1, 0, 0, 255, //green + 0, 0, -1, 0, 255, //blue + 0, 0, 0, 1, 0 //alpha + }; + + return new ColorMatrix(matrix); + } + + public static ColorMatrix adjustBrightness(float brightness) { + brightness = brightness / 100; + ColorMatrix cm = new ColorMatrix(); + cm.setScale(brightness, brightness, brightness, 1); + + return cm; + } + + public static ColorMatrix adjustContrast(float contrast) { + contrast = contrast / 100 + 1; + float o = (-0.5f * contrast + 0.5f) * 255; + float[] matrix = { + contrast, 0, 0, 0, o, //red + 0, contrast, 0, 0, o, //green + 0, 0, contrast, 0, o, //blue + 0, 0, 0, 1, 0 //alpha + }; + + return new ColorMatrix(matrix); + } + + public static ColorMatrix adjustAlpha(float alpha) { + alpha = alpha / 100; + ColorMatrix cm = new ColorMatrix(); + cm.setScale(1, 1, 1, alpha); + + return cm; + } + + public static ColorMatrix applyTint(int color) { + float alpha = Color.alpha(color) / 255f; + float red = Color.red(color) * alpha; + float green = Color.green(color) * alpha; + float blue = Color.blue(color) * alpha; + + float[] matrix = { + 1, 0, 0, 0, red, //red + 0, 1, 0, 0, green, //green + 0, 0, 1, 0, blue, //blue + 0, 0, 0, 1, 0 //alpha + }; + + return new ColorMatrix(matrix); + } + + public static class Builder { + private List<ColorMatrix> mMatrixList; + + public Builder() { + mMatrixList = new ArrayList<ColorMatrix>(); + } + + public Builder hue(float value) { + mMatrixList.add(adjustHue(value)); + return this; + } + + public Builder saturate(float saturation) { + mMatrixList.add(adjustSaturation(saturation)); + return this; + } + + public Builder brightness(float brightness) { + mMatrixList.add(adjustBrightness(brightness)); + return this; + } + + public Builder contrast(float contrast) { + mMatrixList.add(adjustContrast(contrast)); + return this; + } + + public Builder alpha(float alpha) { + mMatrixList.add(adjustAlpha(alpha)); + return this; + } + + public Builder invertColors() { + mMatrixList.add(ColorFilterUtils.invertColors()); + return this; + } + + public Builder tint(int color) { + mMatrixList.add(applyTint(color)); + return this; + } + + public ColorMatrix build() { + if (mMatrixList == null || mMatrixList.size() == 0) return null; + + ColorMatrix colorMatrix = new ColorMatrix(); + for (ColorMatrix cm : mMatrixList) { + colorMatrix.postConcat(cm); + } + return colorMatrix; + } + } + } +} diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 69b8b95..cddcd9f 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -374,6 +374,7 @@ public class Instrumentation { * @see Context#startActivity */ public Activity startActivitySync(Intent intent) { + android.util.SeempLog.record_str(376, intent.toString()); validateNotAppThread(); synchronized (mSync) { @@ -1481,6 +1482,7 @@ public class Instrumentation { public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { + android.util.SeempLog.record_str(377, intent.toString()); IApplicationThread whoThread = (IApplicationThread) contextThread; Uri referrer = target != null ? target.onProvideReferrer() : null; if (referrer != null) { @@ -1541,6 +1543,7 @@ public class Instrumentation { public void execStartActivitiesAsUser(Context who, IBinder contextThread, IBinder token, Activity target, Intent[] intents, Bundle options, int userId) { + android.util.SeempLog.record_str(378, intents.toString()); IApplicationThread whoThread = (IApplicationThread) contextThread; if (mActivityMonitors != null) { synchronized (mSync) { @@ -1604,6 +1607,7 @@ public class Instrumentation { public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, String target, Intent intent, int requestCode, Bundle options) { + android.util.SeempLog.record_str(377, intent.toString()); IApplicationThread whoThread = (IApplicationThread) contextThread; if (mActivityMonitors != null) { synchronized (mSync) { @@ -1664,6 +1668,7 @@ public class Instrumentation { public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options, UserHandle user) { + android.util.SeempLog.record_str(377, intent.toString()); IApplicationThread whoThread = (IApplicationThread) contextThread; if (mActivityMonitors != null) { synchronized (mSync) { @@ -1703,6 +1708,7 @@ public class Instrumentation { Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options, boolean ignoreTargetSecurity, int userId) { + android.util.SeempLog.record_str(379, intent.toString()); IApplicationThread whoThread = (IApplicationThread) contextThread; if (mActivityMonitors != null) { synchronized (mSync) { @@ -1741,6 +1747,7 @@ public class Instrumentation { public void execStartActivityFromAppTask( Context who, IBinder contextThread, IAppTask appTask, Intent intent, Bundle options) { + android.util.SeempLog.record_str(380, intent.toString()); IApplicationThread whoThread = (IApplicationThread) contextThread; if (mActivityMonitors != null) { synchronized (mSync) { diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index c2bf28a..76e55b7 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -546,7 +546,8 @@ public final class LoadedApk { public Resources getResources(ActivityThread mainThread) { if (mResources == null) { mResources = mainThread.getTopLevelResources(mResDir, mSplitResDirs, mOverlayDirs, - mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, null, this); + mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, null, this, + mainThread.getSystemContext(), mPackageName); } return mResources; } @@ -601,7 +602,7 @@ public final class LoadedApk { final int N = packageIdentifiers.size(); for (int i = 0; i < N; i++) { final int id = packageIdentifiers.keyAt(i); - if (id == 0x01 || id == 0x7f) { + if (id == 0x01 || id == 0x7f || id == 0x3f) { continue; } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 6a4f6a1..8835a09 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -45,6 +45,7 @@ import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import android.util.MathUtils; +import android.util.SparseArray; import android.util.TypedValue; import android.view.Gravity; import android.view.View; @@ -62,6 +63,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Objects; +import java.util.Set; /** * A class that represents how a persistent notification is to be presented to @@ -520,6 +523,21 @@ public class Notification implements Parcelable public int priority; /** + * Default. + * Show all notifications from an app on keyguard. + * + * @hide + */ + public static final int SHOW_ALL_NOTI_ON_KEYGUARD = 0x01; + + /** + * Show only notifications from an app which are not ongoing ones. + * + * @hide + */ + public static final int SHOW_NO_ONGOING_NOTI_ON_KEYGUARD = 0x02; + + /** * Accent color (an ARGB integer like the constants in {@link android.graphics.Color}) * to be applied by the standard Style templates when presenting this notification. * @@ -895,6 +913,13 @@ public class Notification implements Parcelable private Icon mLargeIcon; /** + * Used by light picker in Settings to force + * notification lights on when screen is on + * @hide + */ + public static final String EXTRA_FORCE_SHOW_LIGHTS = "android.forceShowLights"; + + /** * Structure to encapsulate a named action that can be shown as part of this notification. * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is * selected by the user. @@ -953,6 +978,9 @@ public class Notification implements Parcelable private Action(Icon icon, CharSequence title, PendingIntent intent, Bundle extras, RemoteInput[] remoteInputs) { this.mIcon = icon; + if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) { + this.icon = icon.getResId(); + } this.title = title; this.actionIntent = intent; this.mExtras = extras != null ? extras : new Bundle(); @@ -1600,13 +1628,23 @@ public class Notification implements Parcelable bigContentView = null; headsUpContentView = null; mLargeIcon = null; - if (extras != null) { - extras.remove(Notification.EXTRA_LARGE_ICON); - extras.remove(Notification.EXTRA_LARGE_ICON_BIG); - extras.remove(Notification.EXTRA_PICTURE); - extras.remove(Notification.EXTRA_BIG_TEXT); + if (extras != null && !extras.isEmpty()) { // Prevent light notifications from being rebuilt. extras.remove(Builder.EXTRA_NEEDS_REBUILD); + final Set<String> keyset = extras.keySet(); + final int N = keyset.size(); + final String[] keys = keyset.toArray(new String[N]); + for (int i=0; i<N; i++) { + final String key = keys[i]; + final Object obj = extras.get(key); + if (obj != null && + ( obj instanceof Parcelable + || obj instanceof Parcelable[] + || obj instanceof SparseArray + || obj instanceof ArrayList)) { + extras.remove(key); + } + } } } @@ -4612,7 +4650,7 @@ public class Notification implements Parcelable * Size value for use with {@link #setCustomSizePreset} to show this notification with * default sizing. * <p>For custom display notifications created using {@link #setDisplayIntent}, - * the default is {@link #SIZE_LARGE}. All other notifications size automatically based + * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based * on their content. */ public static final int SIZE_DEFAULT = 0; diff --git a/core/java/android/app/NotificationGroup.aidl b/core/java/android/app/NotificationGroup.aidl new file mode 100644 index 0000000..44b6290 --- /dev/null +++ b/core/java/android/app/NotificationGroup.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2012, The CyanogenMod 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.app; + +parcelable NotificationGroup; diff --git a/core/java/android/app/NotificationGroup.java b/core/java/android/app/NotificationGroup.java new file mode 100644 index 0000000..bcb70d3 --- /dev/null +++ b/core/java/android/app/NotificationGroup.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2011 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.app; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import android.content.Context; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.text.TextUtils; +import android.util.Log; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** @hide */ +public class NotificationGroup implements Parcelable { + private static final String TAG = "NotificationGroup"; + + private String mName; + private int mNameResId; + + private UUID mUuid; + + private Set<String> mPackages = new HashSet<String>(); + + private boolean mDirty; + + public static final Parcelable.Creator<NotificationGroup> CREATOR = new Parcelable.Creator<NotificationGroup>() { + public NotificationGroup createFromParcel(Parcel in) { + return new NotificationGroup(in); + } + + @Override + public NotificationGroup[] newArray(int size) { + return new NotificationGroup[size]; + } + }; + + public NotificationGroup(String name) { + this(name, -1, null); + } + + public NotificationGroup(String name, int nameResId, UUID uuid) { + mName = name; + mNameResId = nameResId; + mUuid = (uuid != null) ? uuid : UUID.randomUUID(); + mDirty = uuid == null; + } + + private NotificationGroup(Parcel in) { + readFromParcel(in); + } + + @Override + public String toString() { + return getName(); + } + + public String getName() { + return mName; + } + + public void setName(String name) { + mName = name; + mNameResId = -1; + mDirty = true; + } + + public UUID getUuid() { + return mUuid; + } + + public void addPackage(String pkg) { + mPackages.add(pkg); + mDirty = true; + } + + public String[] getPackages() { + return mPackages.toArray(new String[mPackages.size()]); + } + + public void removePackage(String pkg) { + mPackages.remove(pkg); + mDirty = true; + } + + public boolean hasPackage(String pkg) { + return mPackages.contains(pkg); + } + + public boolean isDirty() { + return mDirty; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mName); + dest.writeInt(mNameResId); + dest.writeInt(mDirty ? 1 : 0); + new ParcelUuid(mUuid).writeToParcel(dest, 0); + dest.writeStringArray(getPackages()); + } + + public void readFromParcel(Parcel in) { + mName = in.readString(); + mNameResId = in.readInt(); + mDirty = in.readInt() != 0; + mUuid = ParcelUuid.CREATOR.createFromParcel(in).getUuid(); + mPackages.addAll(Arrays.asList(in.readStringArray())); + } + + public void getXmlString(StringBuilder builder, Context context) { + builder.append("<notificationGroup "); + if (mNameResId > 0) { + builder.append("nameres=\""); + builder.append(context.getResources().getResourceEntryName(mNameResId)); + } else { + builder.append("name=\""); + builder.append(TextUtils.htmlEncode(getName())); + } + builder.append("\" uuid=\""); + builder.append(TextUtils.htmlEncode(getUuid().toString())); + builder.append("\">\n"); + for (String pkg : mPackages) { + builder.append("<package>" + TextUtils.htmlEncode(pkg) + "</package>\n"); + } + builder.append("</notificationGroup>\n"); + mDirty = false; + } + + public static NotificationGroup fromXml(XmlPullParser xpp, Context context) + throws XmlPullParserException, IOException { + String value = xpp.getAttributeValue(null, "nameres"); + int nameResId = -1; + String name = null; + UUID uuid = null; + + if (value != null) { + nameResId = context.getResources().getIdentifier(value, "string", "android"); + if (nameResId > 0) { + name = context.getResources().getString(nameResId); + } + } + + if (name == null) { + name = xpp.getAttributeValue(null, "name"); + } + + value = xpp.getAttributeValue(null, "uuid"); + if (value != null) { + try { + uuid = UUID.fromString(value); + } catch (IllegalArgumentException e) { + Log.w(TAG, "UUID not recognized for " + name + ", using new one."); + } + } + + NotificationGroup notificationGroup = new NotificationGroup(name, nameResId, uuid); + int event = xpp.next(); + while (event != XmlPullParser.END_TAG || !xpp.getName().equals("notificationGroup")) { + if (event == XmlPullParser.START_TAG) { + if (xpp.getName().equals("package")) { + String pkg = xpp.nextText(); + notificationGroup.addPackage(pkg); + } + } + event = xpp.next(); + } + + /* we just loaded from XML, no need to save */ + notificationGroup.mDirty = false; + + return notificationGroup; + } +} diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 605c006..2219e64 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -19,6 +19,7 @@ package android.app; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; +import android.app.Notification; import android.app.Notification.Builder; import android.content.ComponentName; import android.content.Context; @@ -512,6 +513,16 @@ public class NotificationManager return new ArraySet<String>(); } + /** @hide */ + public int getShowNotificationForPackageOnKeyguard(String pkg, int uid) { + INotificationManager service = getService(); + try { + return getService().getShowNotificationForPackageOnKeyguard(pkg, uid); + } catch (RemoteException e) { + return Notification.SHOW_ALL_NOTI_ON_KEYGUARD; + } + } + private Context mContext; private static void checkRequired(String name, Object value) { diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 2117597..c98167d 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -18,22 +18,38 @@ package android.app; import static android.app.ActivityThread.DEBUG_CONFIGURATION; +import android.content.Context; import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; +import android.content.pm.PackageInfo; +import android.content.pm.PackageItemInfo; +import android.content.pm.PackageManager; +import android.content.pm.ThemeUtils; import android.content.res.AssetManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; +import android.content.res.ThemeConfig; import android.content.res.Resources; import android.content.res.ResourcesKey; +import android.graphics.Typeface; import android.hardware.display.DisplayManagerGlobal; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.DisplayMetrics; import android.util.Log; import android.util.Pair; import android.util.Slog; +import android.util.SparseArray; import android.view.Display; import android.view.DisplayAdjustments; import java.lang.ref.WeakReference; +import java.util.List; import java.util.Locale; /** @hide */ @@ -48,9 +64,16 @@ public class ResourcesManager { new ArrayMap<>(); CompatibilityInfo mResCompatibilityInfo; + static IPackageManager sPackageManager; Configuration mResConfiguration; + /** + * Number of default assets attached to a Resource object's AssetManager + * This currently includes framework and cmsdk resources + */ + private static final int NUM_DEFAULT_ASSETS = 2; + public static ResourcesManager getInstance() { synchronized (ResourcesManager.class) { if (sResourcesManager == null) { @@ -156,12 +179,15 @@ public class ResourcesManager { * @param compatInfo the compatibility info. Must not be null. */ Resources getTopLevelResources(String resDir, String[] splitResDirs, - String[] overlayDirs, String[] libDirs, int displayId, - Configuration overrideConfiguration, CompatibilityInfo compatInfo) { + String[] overlayDirs, String[] libDirs, int displayId, String packageName, + Configuration overrideConfiguration, CompatibilityInfo compatInfo, Context context, + boolean isThemeable) { final float scale = compatInfo.applicationScale; + ThemeConfig themeConfig = getThemeConfig(); Configuration overrideConfigCopy = (overrideConfiguration != null) ? new Configuration(overrideConfiguration) : null; - ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfigCopy, scale); + ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale, + isThemeable, getThemeConfig()); Resources r; synchronized (this) { // Resources is app scale dependent. @@ -184,6 +210,8 @@ public class ResourcesManager { //} AssetManager assets = new AssetManager(); + assets.setAppName(packageName); + assets.setThemeSupport(isThemeable); // resDir can be null if the 'android' package is creating a new Resources object. // This is fine, since each AssetManager automatically loads the 'android' package // already. @@ -203,7 +231,7 @@ public class ResourcesManager { if (overlayDirs != null) { for (String idmapPath : overlayDirs) { - assets.addOverlayPath(idmapPath); + assets.addOverlayPath(idmapPath, null, null, null, null); } } @@ -213,7 +241,7 @@ public class ResourcesManager { // Avoid opening files we know do not have resources, // like code-only .jar files. if (assets.addAssetPath(libDir) == 0) { - Log.w(TAG, "Asset path '" + libDir + + Slog.w(TAG, "Asset path '" + libDir + "' does not exist or contains no resources."); } } @@ -237,10 +265,38 @@ public class ResourcesManager { } else { config = getConfiguration(); } + + boolean iconsAttached = false; + /* Attach theme information to the resulting AssetManager when appropriate. */ + if (config != null && !context.getPackageManager().isSafeMode()) { + if (themeConfig == null) { + try { + themeConfig = ThemeConfig.getBootTheme(context.getContentResolver()); + } catch (Exception e) { + Slog.d(TAG, "ThemeConfig.getBootTheme failed, falling back to system theme", e); + themeConfig = ThemeConfig.getSystemTheme(); + } + } + + if (isThemeable) { + if (themeConfig != null) { + attachThemeAssets(assets, themeConfig); + attachCommonAssets(assets, themeConfig); + iconsAttached = attachIconAssets(assets, themeConfig); + } + } else if (themeConfig != null && + !ThemeConfig.SYSTEM_DEFAULT.equals(themeConfig.getFontPkgName())) { + // use system fonts if not themeable and a theme font is currently in use + Typeface.recreateDefaults(true); + } + } + r = new Resources(assets, dm, config, compatInfo); + if (iconsAttached) setActivityIcons(r); if (DEBUG) Slog.i(TAG, "Created app resources " + resDir + " " + r + ": " + r.getConfiguration() + " appScale=" + r.getCompatibilityInfo().applicationScale); + synchronized (this) { WeakReference<Resources> wr = mActiveResources.get(key); Resources existing = wr != null ? wr.get() : null; @@ -258,6 +314,152 @@ public class ResourcesManager { } } + /** + * Creates the top level Resources for applications with the given compatibility info. + * + * @param resDir the resource directory. + * @param compatInfo the compability info. Must not be null. + * + * @hide + */ + public Resources getTopLevelThemedResources(String resDir, int displayId, String packageName, + String themePackageName, CompatibilityInfo compatInfo, boolean isThemeable) { + Resources r; + + ThemeConfig.Builder builder = new ThemeConfig.Builder(); + builder.defaultOverlay(themePackageName); + builder.defaultIcon(themePackageName); + builder.defaultFont(themePackageName); + ThemeConfig themeConfig = builder.build(); + + ResourcesKey key = new ResourcesKey(resDir, displayId, null, compatInfo.applicationScale, + isThemeable, themeConfig); + + synchronized (this) { + WeakReference<Resources> wr = mActiveResources.get(key); + r = wr != null ? wr.get() : null; + if (r != null && r.getAssets().isUpToDate()) { + if (false) { + Slog.w(TAG, "Returning cached resources " + r + " " + resDir + + ": appScale=" + r.getCompatibilityInfo().applicationScale); + } + return r; + } + } + + AssetManager assets = new AssetManager(); + assets.setAppName(packageName); + assets.setThemeSupport(isThemeable); + if (assets.addAssetPath(resDir) == 0) { + return null; + } + + //Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics); + DisplayMetrics dm = getDisplayMetricsLocked(displayId); + Configuration config; + boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY); + final boolean hasOverrideConfig = key.hasOverrideConfiguration(); + if (!isDefaultDisplay || hasOverrideConfig) { + config = new Configuration(getConfiguration()); + if (!isDefaultDisplay) { + applyNonDefaultDisplayMetricsToConfigurationLocked(dm, config); + } + if (hasOverrideConfig) { + config.updateFrom(key.mOverrideConfiguration); + } + } else { + config = getConfiguration(); + } + + boolean iconsAttached = false; + if (isThemeable) { + /* Attach theme information to the resulting AssetManager when appropriate. */ + attachThemeAssets(assets, themeConfig); + attachCommonAssets(assets, themeConfig); + iconsAttached = attachIconAssets(assets, themeConfig); + } + r = new Resources(assets, dm, config, compatInfo); + if (iconsAttached) setActivityIcons(r); + + if (false) { + Slog.i(TAG, "Created THEMED app resources " + resDir + " " + r + ": " + + r.getConfiguration() + " appScale=" + + r.getCompatibilityInfo().applicationScale); + } + + synchronized (this) { + WeakReference<Resources> wr = mActiveResources.get(key); + Resources existing = wr != null ? wr.get() : null; + if (existing != null && existing.getAssets().isUpToDate()) { + // Someone else already created the resources while we were + // unlocked; go ahead and use theirs. + r.getAssets().close(); + return existing; + } + + // XXX need to remove entries when weak references go away + mActiveResources.put(key, new WeakReference<Resources>(r)); + return r; + } + } + + /** + * Creates a map between an activity & app's icon ids to its component info. This map + * is then stored in the resource object. + * When resource.getDrawable(id) is called it will check this mapping and replace + * the id with the themed resource id if one is available + * @param r + */ + private void setActivityIcons(Resources r) { + SparseArray<PackageItemInfo> iconResources = new SparseArray<PackageItemInfo>(); + String pkgName = r.getAssets().getAppName(); + PackageInfo pkgInfo = null; + ApplicationInfo appInfo = null; + + try { + pkgInfo = getPackageManager().getPackageInfo(pkgName, PackageManager.GET_ACTIVITIES, + UserHandle.getCallingUserId()); + } catch (RemoteException e1) { + Slog.e(TAG, "Unable to get pkg " + pkgName, e1); + return; + } + + final ThemeConfig themeConfig = r.getConfiguration().themeConfig; + if (pkgName != null && themeConfig != null && + pkgName.equals(themeConfig.getIconPackPkgName())) { + return; + } + + //Map application icon + if (pkgInfo != null && pkgInfo.applicationInfo != null) { + appInfo = pkgInfo.applicationInfo; + if (appInfo.themedIcon != 0 || iconResources.get(appInfo.icon) == null) { + iconResources.put(appInfo.icon, appInfo); + } + } + + //Map activity icons. + if (pkgInfo != null && pkgInfo.activities != null) { + for (ActivityInfo ai : pkgInfo.activities) { + if (ai.icon != 0 && (ai.themedIcon != 0 || iconResources.get(ai.icon) == null)) { + iconResources.put(ai.icon, ai); + } else if (appInfo != null && appInfo.icon != 0 && + (ai.themedIcon != 0 || iconResources.get(appInfo.icon) == null)) { + iconResources.put(appInfo.icon, ai); + } + } + } + + r.setIconResources(iconResources); + final IPackageManager pm = getPackageManager(); + try { + ComposedIconInfo iconInfo = pm.getComposedIconInfo(); + r.setComposedIconInfo(iconInfo); + } catch (Exception e) { + Slog.wtf(TAG, "Failed to retrieve ComposedIconInfo", e); + } + } + final boolean applyConfigurationToResourcesLocked(Configuration config, CompatibilityInfo compat) { if (mResConfiguration == null) { @@ -303,6 +505,22 @@ public class ResourcesManager { boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY); DisplayMetrics dm = defaultDisplayMetrics; final boolean hasOverrideConfiguration = key.hasOverrideConfiguration(); + boolean themeChanged = (changes & ActivityInfo.CONFIG_THEME_RESOURCE) != 0; + if (themeChanged) { + AssetManager am = r.getAssets(); + if (am.hasThemeSupport()) { + r.setIconResources(null); + r.setComposedIconInfo(null); + detachThemeAssets(am); + if (config.themeConfig != null) { + attachThemeAssets(am, config.themeConfig); + attachCommonAssets(am, config.themeConfig); + if (attachIconAssets(am, config.themeConfig)) { + setActivityIcons(r); + } + } + } + } if (!isDefaultDisplay || hasOverrideConfiguration) { if (tmpConfig == null) { tmpConfig = new Configuration(); @@ -319,6 +537,9 @@ public class ResourcesManager { } else { r.updateConfiguration(config, dm, compat); } + if (themeChanged) { + r.updateStringCache(); + } //Slog.i(TAG, "Updated app resources " + v.getKey() // + " " + r + ": " + r.getConfiguration()); } else { @@ -330,4 +551,265 @@ public class ResourcesManager { return changes != 0; } + public static IPackageManager getPackageManager() { + if (sPackageManager != null) { + return sPackageManager; + } + IBinder b = ServiceManager.getService("package"); + sPackageManager = IPackageManager.Stub.asInterface(b); + return sPackageManager; + } + + + /** + * Attach the necessary theme asset paths and meta information to convert an + * AssetManager to being globally "theme-aware". + * + * @param assets + * @param theme + * @return true if the AssetManager is now theme-aware; false otherwise. + * This can fail, for example, if the theme package has been been + * removed and the theme manager has yet to revert formally back to + * the framework default. + */ + private boolean attachThemeAssets(AssetManager assets, ThemeConfig theme) { + PackageInfo piTheme = null; + PackageInfo piTarget = null; + PackageInfo piAndroid = null; + PackageInfo piCm = null; + + // Some apps run in process of another app (eg keyguard/systemUI) so we must get the + // package name from the res tables. The 0th base package name will be the android group. + // The 1st base package name will be the app group if one is attached. Check if it is there + // first or else the system will crash! + String basePackageName = null; + String resourcePackageName = null; + int count = assets.getBasePackageCount(); + if (count > NUM_DEFAULT_ASSETS) { + basePackageName = assets.getBasePackageName(NUM_DEFAULT_ASSETS); + resourcePackageName = assets.getBaseResourcePackageName(NUM_DEFAULT_ASSETS); + } else if (count == NUM_DEFAULT_ASSETS) { + basePackageName = assets.getBasePackageName(0); + } else { + return false; + } + + try { + piTheme = getPackageManager().getPackageInfo( + theme.getOverlayPkgNameForApp(basePackageName), 0, + UserHandle.getCallingUserId()); + piTarget = getPackageManager().getPackageInfo( + basePackageName, 0, UserHandle.getCallingUserId()); + + // Handle special case where a system app (ex trebuchet) may have had its pkg name + // renamed during an upgrade. basePackageName would be the manifest value which will + // fail on getPackageInfo(). resource pkg is assumed to have the original name + if (piTarget == null && resourcePackageName != null) { + piTarget = getPackageManager().getPackageInfo(resourcePackageName, + 0, UserHandle.getCallingUserId()); + } + piAndroid = getPackageManager().getPackageInfo("android", 0, + UserHandle.getCallingUserId()); + piCm = getPackageManager().getPackageInfo("cyanogenmod.platform", 0, + UserHandle.getCallingUserId()); + } catch (RemoteException e) { + } + + if (piTheme == null || piTheme.applicationInfo == null || + piTarget == null || piTarget.applicationInfo == null || + piAndroid == null || piAndroid.applicationInfo == null || + piCm == null || piCm.applicationInfo == null || + piTheme.mOverlayTargets == null) { + return false; + } + + // Attach themed resources for target + String themePackageName = piTheme.packageName; + String themePath = piTheme.applicationInfo.publicSourceDir; + if (!piTarget.isThemeApk && piTheme.mOverlayTargets.contains(basePackageName)) { + String targetPackagePath = piTarget.applicationInfo.sourceDir; + String prefixPath = ThemeUtils.getOverlayPathToTarget(basePackageName); + + String resCachePath = ThemeUtils.getTargetCacheDir(piTarget.packageName, + piTheme.packageName); + String resApkPath = resCachePath + "/resources.apk"; + String idmapPath = ThemeUtils.getIdmapPath(piTarget.packageName, piTheme.packageName); + int cookie = assets.addOverlayPath(idmapPath, themePath, resApkPath, + targetPackagePath, prefixPath); + + if (cookie != 0) { + assets.setThemePackageName(themePackageName); + assets.addThemeCookie(cookie); + } + } + + // Attach themed resources for cmsdk + if (!piTarget.isThemeApk && !piCm.packageName.equals(basePackageName) && + piTheme.mOverlayTargets.contains(piCm.packageName)) { + String resCachePath= ThemeUtils.getTargetCacheDir(piCm.packageName, + piTheme.packageName); + String prefixPath = ThemeUtils.getOverlayPathToTarget(piCm.packageName); + String targetPackagePath = piCm.applicationInfo.publicSourceDir; + String resApkPath = resCachePath + "/resources.apk"; + String idmapPath = ThemeUtils.getIdmapPath(piCm.packageName, piTheme.packageName); + int cookie = assets.addOverlayPath(idmapPath, themePath, + resApkPath, targetPackagePath, prefixPath); + if (cookie != 0) { + assets.setThemePackageName(themePackageName); + assets.addThemeCookie(cookie); + } + } + + // Attach themed resources for android framework + if (!piTarget.isThemeApk && !"android".equals(basePackageName) && + piTheme.mOverlayTargets.contains("android")) { + String resCachePath= ThemeUtils.getTargetCacheDir(piAndroid.packageName, + piTheme.packageName); + String prefixPath = ThemeUtils.getOverlayPathToTarget(piAndroid.packageName); + String targetPackagePath = piAndroid.applicationInfo.publicSourceDir; + String resApkPath = resCachePath + "/resources.apk"; + String idmapPath = ThemeUtils.getIdmapPath("android", piTheme.packageName); + int cookie = assets.addOverlayPath(idmapPath, themePath, + resApkPath, targetPackagePath, prefixPath); + if (cookie != 0) { + assets.setThemePackageName(themePackageName); + assets.addThemeCookie(cookie); + } + } + + return true; + } + + /** + * Attach the necessary icon asset paths. Icon assets should be in a different + * namespace than the standard 0x7F. + * + * @param assets + * @param theme + * @return true if succes, false otherwise + */ + private boolean attachIconAssets(AssetManager assets, ThemeConfig theme) { + PackageInfo piIcon = null; + try { + piIcon = getPackageManager().getPackageInfo(theme.getIconPackPkgName(), 0, + UserHandle.getCallingUserId()); + } catch (RemoteException e) { + } + + if (piIcon == null || piIcon.applicationInfo == null) { + return false; + } + + String iconPkg = theme.getIconPackPkgName(); + if (iconPkg != null && !iconPkg.isEmpty()) { + String themeIconPath = piIcon.applicationInfo.publicSourceDir; + String prefixPath = ThemeUtils.ICONS_PATH; + String iconDir = ThemeUtils.getIconPackDir(iconPkg); + String resTablePath = iconDir + "/resources.arsc"; + String resApkPath = iconDir + "/resources.apk"; + + // Legacy Icon packs have everything in their APK + if (piIcon.isLegacyIconPackApk) { + prefixPath = ""; + resApkPath = ""; + resTablePath = ""; + } + + int cookie = assets.addIconPath(themeIconPath, resApkPath, prefixPath, + Resources.THEME_ICON_PKG_ID); + if (cookie != 0) { + assets.setIconPackCookie(cookie); + assets.setIconPackageName(iconPkg); + } + } + + return true; + } + + /** + * Attach the necessary common asset paths. Common assets should be in a different + * namespace than the standard 0x7F. + * + * @param assets + * @param theme + * @return true if succes, false otherwise + */ + private boolean attachCommonAssets(AssetManager assets, ThemeConfig theme) { + // Some apps run in process of another app (eg keyguard/systemUI) so we must get the + // package name from the res tables. The 0th base package name will be the android group. + // The 1st base package name will be the app group if one is attached. Check if it is there + // first or else the system will crash! + String basePackageName; + int count = assets.getBasePackageCount(); + if (count > NUM_DEFAULT_ASSETS) { + basePackageName = assets.getBasePackageName(NUM_DEFAULT_ASSETS); + } else if (count == NUM_DEFAULT_ASSETS) { + basePackageName = assets.getBasePackageName(0); + } else { + return false; + } + + PackageInfo piTheme = null; + try { + piTheme = getPackageManager().getPackageInfo( + theme.getOverlayPkgNameForApp(basePackageName), 0, + UserHandle.getCallingUserId()); + } catch (RemoteException e) { + } + + if (piTheme == null || piTheme.applicationInfo == null) { + return false; + } + + String themePackageName = + ThemeUtils.getCommonPackageName(piTheme.applicationInfo.packageName); + if (themePackageName != null && !themePackageName.isEmpty()) { + String themePath = piTheme.applicationInfo.publicSourceDir; + String prefixPath = ThemeUtils.COMMON_RES_PATH; + String resCachePath = + ThemeUtils.getTargetCacheDir(ThemeUtils.COMMON_RES_TARGET, piTheme.packageName); + String resApkPath = resCachePath + "/resources.apk"; + int cookie = assets.addCommonOverlayPath(themePath, resApkPath, + prefixPath); + if (cookie != 0) { + assets.setCommonResCookie(cookie); + assets.setCommonResPackageName(themePackageName); + } + } + + return true; + } + + private void detachThemeAssets(AssetManager assets) { + String themePackageName = assets.getThemePackageName(); + String iconPackageName = assets.getIconPackageName(); + String commonResPackageName = assets.getCommonResPackageName(); + + //Remove Icon pack if it exists + if (!TextUtils.isEmpty(iconPackageName) && assets.getIconPackCookie() > 0) { + assets.removeOverlayPath(iconPackageName, assets.getIconPackCookie()); + assets.setIconPackageName(null); + assets.setIconPackCookie(0); + } + //Remove common resources if it exists + if (!TextUtils.isEmpty(commonResPackageName) && assets.getCommonResCookie() > 0) { + assets.removeOverlayPath(commonResPackageName, assets.getCommonResCookie()); + assets.setCommonResPackageName(null); + assets.setCommonResCookie(0); + } + final List<Integer> themeCookies = assets.getThemeCookies(); + if (!TextUtils.isEmpty(themePackageName) && !themeCookies.isEmpty()) { + // remove overlays in reverse order + for (int i = themeCookies.size() - 1; i >= 0; i--) { + assets.removeOverlayPath(themePackageName, themeCookies.get(i)); + } + } + assets.getThemeCookies().clear(); + assets.setThemePackageName(null); + } + + private ThemeConfig getThemeConfig() { + final Configuration config = getConfiguration(); + return config != null ? config.themeConfig : null; + } } diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 5e8ad68..fad3f62 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -92,6 +92,7 @@ public class StatusBarManager { public static final int CAMERA_LAUNCH_SOURCE_WIGGLE = 0; public static final int CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP = 1; + public static final int CAMERA_LAUNCH_SOURCE_SCREEN_GESTURE = 2; private Context mContext; private IStatusBarService mService; diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 3d264c6..34c967f 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -254,7 +254,9 @@ final class SystemServiceRegistry { new StaticServiceFetcher<BatteryManager>() { @Override public BatteryManager createService() { - return new BatteryManager(); + IBinder b = ServiceManager.getService(Context.BATTERY_SERVICE); + IBatteryService service = IBatteryService.Stub.asInterface(b); + return new BatteryManager(service); }}); registerService(Context.NFC_SERVICE, NfcManager.class, diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 22e79b6..045ee39 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -33,6 +33,7 @@ import android.graphics.ColorFilter; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PixelFormat; +import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; @@ -230,9 +231,10 @@ public class WallpaperManager { private IWallpaperManager mService; private Bitmap mWallpaper; private Bitmap mDefaultWallpaper; + private Bitmap mKeyguardWallpaper; private static final int MSG_CLEAR_WALLPAPER = 1; - + Globals(Looper looper) { IBinder b = ServiceManager.getService(Context.WALLPAPER_SERVICE); mService = IWallpaperManager.Stub.asInterface(b); @@ -250,6 +252,12 @@ public class WallpaperManager { } } + public void onKeyguardWallpaperChanged() { + synchronized (this) { + mKeyguardWallpaper = null; + } + } + public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault) { synchronized (this) { if (mService != null) { @@ -285,6 +293,23 @@ public class WallpaperManager { } } + /** + * @hide + */ + public Bitmap peekKeyguardWallpaperBitmap(Context context) { + synchronized (this) { + if (mKeyguardWallpaper != null) { + return mKeyguardWallpaper; + } + try { + mKeyguardWallpaper = getCurrentKeyguardWallpaperLocked(context); + } catch (OutOfMemoryError e) { + Log.w(TAG, "No memory load current keyguard wallpaper", e); + } + return mKeyguardWallpaper; + } + } + public void forgetLoadedWallpaper() { synchronized (this) { mWallpaper = null; @@ -292,6 +317,12 @@ public class WallpaperManager { } } + public void forgetLoadedKeyguardWallpaper() { + synchronized (this) { + mKeyguardWallpaper = null; + } + } + private Bitmap getCurrentWallpaperLocked(Context context) { if (mService == null) { Log.w(TAG, "WallpaperService not running"); @@ -321,7 +352,37 @@ public class WallpaperManager { } return null; } - + + private Bitmap getCurrentKeyguardWallpaperLocked(Context context) { + if (mService == null) { + Log.w(TAG, "WallpaperService not running"); + return null; + } + try { + Bundle params = new Bundle(); + ParcelFileDescriptor fd = mService.getKeyguardWallpaper(this, params); + if (fd != null) { + try { + BitmapFactory.Options options = new BitmapFactory.Options(); + Bitmap bm = BitmapFactory.decodeFileDescriptor( + fd.getFileDescriptor(), null, options); + return bm; + } catch (OutOfMemoryError e) { + Log.w(TAG, "Can't decode file", e); + } finally { + try { + fd.close(); + } catch (IOException e) { + // Ignore + } + } + } + } catch (RemoteException e) { + // Ignore + } + return null; + } + private Bitmap getDefaultWallpaperLocked(Context context) { InputStream is = openDefaultWallpaper(context); if (is != null) { @@ -340,6 +401,18 @@ public class WallpaperManager { } return null; } + + /** @hide */ + public void clearKeyguardWallpaper() { + synchronized (this) { + try { + mService.clearKeyguardWallpaper(); + } catch (RemoteException e) { + // ignore + } + mKeyguardWallpaper = null; + } + } } private static final Object sSync = new Object[0]; @@ -599,6 +672,15 @@ public class WallpaperManager { return null; } + /** @hide */ + public Drawable getFastKeyguardDrawable() { + Bitmap bm = sGlobals.peekKeyguardWallpaperBitmap(mContext); + if (bm != null) { + return new FastBitmapDrawable(bm); + } + return null; + } + /** * Like {@link #getFastDrawable()}, but if there is no wallpaper set, * a null pointer is returned. @@ -624,6 +706,13 @@ public class WallpaperManager { } /** + * @hide + */ + public Bitmap getKeyguardBitmap() { + return sGlobals.peekKeyguardWallpaperBitmap(mContext); + } + + /** * Remove all internal references to the last loaded wallpaper. Useful * for apps that want to reduce memory usage when they only temporarily * need to have the wallpaper. After calling, the next request for the @@ -636,6 +725,13 @@ public class WallpaperManager { } /** + * @hide + */ + public void forgetLoadedKeyguardWallpaper() { + sGlobals.forgetLoadedKeyguardWallpaper(); + } + + /** * If the current wallpaper is a live wallpaper component, return the * information about that wallpaper. Otherwise, if it is a static image, * simply return null. @@ -787,6 +883,36 @@ public class WallpaperManager { } /** + * @param bitmap + * @throws IOException + * @hide + */ + public void setKeyguardBitmap(Bitmap bitmap) throws IOException { + if (sGlobals.mService == null) { + Log.w(TAG, "WallpaperService not running"); + return; + } + try { + ParcelFileDescriptor fd = sGlobals.mService.setKeyguardWallpaper(null, + mContext.getOpPackageName()); + if (fd == null) { + return; + } + FileOutputStream fos = null; + try { + fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); + bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos); + } finally { + if (fos != null) { + fos.close(); + } + } + } catch (RemoteException e) { + // Ignore + } + } + + /** * Change the current system wallpaper to a specific byte stream. The * give InputStream is copied into persistent storage and will now be * used as the wallpaper. Currently it must be either a JPEG or PNG @@ -826,6 +952,34 @@ public class WallpaperManager { } } + /** + * @hide + */ + public void setKeyguardStream(InputStream data) throws IOException { + if (sGlobals.mService == null) { + Log.w(TAG, "WallpaperService not running"); + return; + } + try { + ParcelFileDescriptor fd = sGlobals.mService.setKeyguardWallpaper(null, + mContext.getOpPackageName()); + if (fd == null) { + return; + } + FileOutputStream fos = null; + try { + fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd); + setWallpaper(data, fos); + } finally { + if (fos != null) { + fos.close(); + } + } + } catch (RemoteException e) { + // Ignore + } + } + private void setWallpaper(InputStream data, FileOutputStream fos) throws IOException { byte[] buffer = new byte[32768]; @@ -1088,7 +1242,29 @@ public class WallpaperManager { mWallpaperXStep = xStep; mWallpaperYStep = yStep; } - + + /** @hide */ + public int getLastWallpaperX() { + try { + return WindowManagerGlobal.getWindowSession().getLastWallpaperX(); + } catch (RemoteException e) { + // Ignore. + } + + return -1; + } + + /** @hide */ + public int getLastWallpaperY() { + try { + return WindowManagerGlobal.getWindowSession().getLastWallpaperY(); + } catch (RemoteException e) { + // Ignore. + } + + return -1; + } + /** * Send an arbitrary command to the current active wallpaper. * @@ -1162,7 +1338,25 @@ public class WallpaperManager { * wallpaper. */ public void clear() throws IOException { - setStream(openDefaultWallpaper(mContext)); + clear(true); + } + + /** @hide */ + public void clear(boolean setToDefault) throws IOException { + if (setToDefault) { + setStream(openDefaultWallpaper(mContext)); + } else { + Bitmap blackBmp = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565); + blackBmp.setPixel(0, 0, mContext.getResources().getColor(android.R.color.black)); + setBitmap(blackBmp); + } + } + + /** + * @hide + */ + public void clearKeyguardWallpaper() { + sGlobals.clearKeyguardWallpaper(); } /** diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index e3414d9..5b9d9d5 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -4461,4 +4461,24 @@ public class DevicePolicyManager { return PERMISSION_GRANT_STATE_DEFAULT; } } + + /** + * CM: check if secure keyguard is required + * @hide + */ + public boolean requireSecureKeyguard() { + return requireSecureKeyguard(UserHandle.myUserId()); + } + + /** @hide */ + public boolean requireSecureKeyguard(int userHandle) { + if (mService != null) { + try { + return mService.requireSecureKeyguard(userHandle); + } catch (RemoteException e) { + Log.w(TAG, "Failed to get secure keyguard requirement"); + } + } + return true; + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 376a3d8..a40507b 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -234,4 +234,6 @@ interface IDevicePolicyManager { boolean setPermissionGrantState(in ComponentName admin, String packageName, String permission, int grantState); int getPermissionGrantState(in ComponentName admin, String packageName, String permission); + + boolean requireSecureKeyguard(int userHandle); } diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java index 7718a36..0335e28 100644 --- a/core/java/android/app/backup/FullBackup.java +++ b/core/java/android/app/backup/FullBackup.java @@ -21,6 +21,8 @@ import android.content.pm.PackageManager; import android.content.res.XmlResourceParser; import android.os.*; import android.os.Process; +import android.os.storage.StorageManager; +import android.os.storage.StorageVolume; import android.system.ErrnoException; import android.system.Os; import android.text.TextUtils; @@ -207,6 +209,8 @@ public class FullBackup { final int mFullBackupContent; final PackageManager mPackageManager; + final StorageManager mStorageManager; + final StorageVolume[] mVolumes; final String mPackageName; /** @@ -230,6 +234,15 @@ public class FullBackup { } else { return null; } + } else if (domainToken.startsWith(FullBackup.SHARED_PREFIX)) { + int slash = domainToken.indexOf('/'); + int i = Integer.parseInt(domainToken.substring(slash + 1)); + + if (i < mVolumes.length) { + return mVolumes[i].getPath(); + } else { + Log.e(TAG, "Could not find volume for " + domainToken); + } } else if (domainToken.equals(FullBackup.NO_BACKUP_TREE_TOKEN)) { return NOBACKUP_DIR.getCanonicalPath(); } @@ -263,6 +276,8 @@ public class FullBackup { SHAREDPREF_DIR = context.getSharedPrefsFile("foo").getParentFile(); CACHE_DIR = context.getCacheDir(); NOBACKUP_DIR = context.getNoBackupFilesDir(); + mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); + mVolumes = mStorageManager.getVolumeList(); if (android.os.Process.myUid() != Process.SYSTEM_UID) { EXTERNAL_DIR = context.getExternalFilesDir(null); } else { diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java index 2e27345..74302f2 100644..100755 --- a/core/java/android/bluetooth/BluetoothA2dpSink.java +++ b/core/java/android/bluetooth/BluetoothA2dpSink.java @@ -371,6 +371,89 @@ public final class BluetoothA2dpSink implements BluetoothProfile { } /** + * Set priority of the profile + * + * <p> The device should already be paired. + * Priority can be one of {@link #PRIORITY_ON} orgetBluetoothManager + * {@link #PRIORITY_OFF}, + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Paired bluetooth device + * @param priority + * @return true if priority is set, false on error + * @hide + */ + public boolean setPriority(BluetoothDevice device, int priority) { + if (DBG) log("setPriority(" + device + ", " + priority + ")"); + if (mService != null && isEnabled() + && isValidDevice(device)) { + if (priority != BluetoothProfile.PRIORITY_OFF && + priority != BluetoothProfile.PRIORITY_ON){ + return false; + } + try { + return mService.setPriority(device, priority); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Get the priority of the profile. + * + * <p> The priority can be any of: + * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF}, + * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Bluetooth device + * @return priority of the device + * @hide + */ + public int getPriority(BluetoothDevice device) { + if (VDBG) log("getPriority(" + device + ")"); + if (mService != null && isEnabled() + && isValidDevice(device)) { + try { + return mService.getPriority(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.PRIORITY_OFF; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.PRIORITY_OFF; + } + + /** + * Check if A2DP profile is streaming music. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device BluetoothDevice device + */ + public boolean isA2dpPlaying(BluetoothDevice device) { + if (mService != null && isEnabled() + && isValidDevice(device)) { + try { + return mService.isA2dpPlaying(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** * Helper for converting a state to a string. * * For debug use only - strings are not internationalized. diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 1f3ff51..71183d9 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -1,6 +1,8 @@ /* * Copyright (C) 2009-2015 The Android Open Source Project * Copyright (C) 2015 Samsung LSI + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Not a Contribution. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +25,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; +import android.app.ActivityThread; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; @@ -92,7 +95,7 @@ import java.util.UUID; */ public final class BluetoothAdapter { private static final String TAG = "BluetoothAdapter"; - private static final boolean DBG = true; + private static final boolean DBG = false; private static final boolean VDBG = false; /** @@ -538,6 +541,7 @@ public final class BluetoothAdapter { * @throws IllegalArgumentException if address is invalid */ public BluetoothDevice getRemoteDevice(String address) { + android.util.SeempLog.record(62); return new BluetoothDevice(address); } @@ -553,6 +557,7 @@ public final class BluetoothAdapter { * @throws IllegalArgumentException if address is invalid */ public BluetoothDevice getRemoteDevice(byte[] address) { + android.util.SeempLog.record(62); if (address == null || address.length != 6) { throw new IllegalArgumentException("Bluetooth address must have 6 bytes"); } @@ -762,7 +767,7 @@ public final class BluetoothAdapter { try { if (DBG) Log.d(TAG, "Calling enableBLE"); mManagerService.updateBleAppCount(mToken, true); - return mManagerService.enable(); + return mManagerService.enable(ActivityThread.currentPackageName()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -784,6 +789,7 @@ public final class BluetoothAdapter { @RequiresPermission(Manifest.permission.BLUETOOTH) @AdapterState public int getState() { + android.util.SeempLog.record(63); try { synchronized(mManagerCallback) { if (mService != null) @@ -880,6 +886,7 @@ public final class BluetoothAdapter { */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean enable() { + android.util.SeempLog.record(56); int state = STATE_OFF; if (isEnabled() == true){ if (DBG) Log.d(TAG, "enable(): BT is already enabled..!"); @@ -898,7 +905,7 @@ public final class BluetoothAdapter { return true; } try { - return mManagerService.enable(); + return mManagerService.enable(ActivityThread.currentPackageName()); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -929,6 +936,7 @@ public final class BluetoothAdapter { */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean disable() { + android.util.SeempLog.record(57); try { return mManagerService.disable(true); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -946,6 +954,7 @@ public final class BluetoothAdapter { * @hide */ public boolean disable(boolean persist) { + android.util.SeempLog.record(57); try { return mManagerService.disable(persist); @@ -1190,6 +1199,7 @@ public final class BluetoothAdapter { */ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean startDiscovery() { + android.util.SeempLog.record(58); if (getState() != STATE_ON) return false; try { synchronized(mManagerCallback) { @@ -1408,6 +1418,7 @@ public final class BluetoothAdapter { */ @RequiresPermission(Manifest.permission.BLUETOOTH) public Set<BluetoothDevice> getBondedDevices() { + android.util.SeempLog.record(61); if (getState() != STATE_ON) { return toDeviceSet(new BluetoothDevice[0]); } @@ -1460,6 +1471,7 @@ public final class BluetoothAdapter { */ @RequiresPermission(Manifest.permission.BLUETOOTH) public int getProfileConnectionState(int profile) { + android.util.SeempLog.record(64); if (getState() != STATE_ON) return BluetoothProfile.STATE_DISCONNECTED; try { synchronized(mManagerCallback) { @@ -1582,6 +1594,7 @@ public final class BluetoothAdapter { @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid) throws IOException { + android.util.SeempLog.record(59); return createNewRfcommSocketAndRecord(name, uuid, false, false); } @@ -1754,6 +1767,117 @@ public final class BluetoothAdapter { return listenUsingL2capOn(port, false, false); } + + /** + * Construct an insecure L2CAP server socket. + * Call #accept to retrieve connections to this socket. + * <p>To auto assign a port without creating a SDP record use + * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number. + * @param port the PSM to listen on + * @return An L2CAP BluetoothServerSocket + * @throws IOException On error, for example Bluetooth not available, or + * insufficient permissions. + * @hide + */ + public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException { + BluetoothServerSocket socket = new BluetoothServerSocket( + BluetoothSocket.TYPE_L2CAP, false, false, port, false, false); + int errno = socket.mSocket.bindListen(); + if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) { + socket.setChannel(socket.mSocket.getPort()); + } + if (errno != 0) { + //TODO(BT): Throw the same exception error code + // that the previous code was using. + //socket.mSocket.throwErrnoNative(errno); + throw new IOException("Error: " + errno); + } + return socket; + + } + + /** + * Create a client side Message Access Profile Service Record. + * Create the record once, and reuse it for all connections. + * If changes to a record is needed remove the old record using {@link removeSdpRecord} + * and then create a new one. + * WARNING: This API requires removeSdpRecord() to be called, to avoid leaking resources! + * A second call to this function - either from two different apps or from the + * same app, without first calling removeSdpRecord() - will make the device + * break the Bluetooth spec, which could lead to severe IOP issues. + * @param serviceName The textual name of the service + * @param rfcommChannel The RFCOMM channel that clients can connect to + * (obtain from BluetoothServerSocket) + * @param l2capPsm The L2CAP PSM channel that clients can connect to + * (obtain from BluetoothServerSocket) + * Supply -1 to omit the L2CAP PSM from the record. + * @param version The Profile version number (As specified in the Bluetooth + * MAP specification) + * @param features The feature bit mask (As specified in the Bluetooth + * MAP specification) + * @return a handle to the record created. The record can be removed again + * using {@link removeSdpRecord}(). The record is not linked to the + * creation/destruction of BluetoothSockets, hence SDP record cleanup + * is a separate process. + * returns -1 if an error occure and the record was not created. + * @hide + */ + public int createMapMnsSdpRecord(String serviceName, int rfcommChannel, + int l2capPsm, int version, int features) { + try { + return mService.createMapMnsSdpRecord(serviceName, rfcommChannel, + l2capPsm, version, features); + } catch (RemoteException e) { + Log.e(TAG, "createMapMnsSdpRecord: ", e); + } + return -1; + } + + /** + * Create a client side Phonebook Access Profile Service Record. + * Create the record once, and reuse it for all connections. + * If changes to a record is needed remove the old record using {@link removeSdpRecord} + * and then create a new one. + * WARNING: This API requires removeSdpRecord() to be called, to avoid leaking resources! + * A second call to this function - either from two different apps or from the + * same app, without first calling removeSdpRecord() - will make the device + * break the Bluetooth spec, which could lead to severe IOP issues. + * @param serviceName The textual name of the service + * @param version The Profile version number (As specified in the Bluetooth + * PBAP specification) + * @return a handle to the record created. The record can be removed again + * using {@link removeSdpRecord}(). The record is not linked to the + * creation/destruction of BluetoothSockets, hence SDP record cleanup + * is a separate process. + * returns -1 if an error occure and the record was not created. + * @hide + */ + public int createPbapPceSdpRecord(String serviceName, int version) { + try { + return mService.createPbapPceSdpRecord(serviceName, version); + } catch (RemoteException e) { + Log.e(TAG, "createPbapPceSdpRecord: ", e); + } + return -1; + } + + /** + * Remove a SDP record created using createSdpRecord(). + * This function shall be called before a new call to createSdpRecord for the same record + * type can be made, unless the record type created supports multiple instances. + * @param recordHandle handle of the record to remove - provided by createSdpRecord() + * @return true if success + * @hide + */ + public boolean removeSdpRecord(int recordHandle){ + try { + return mService.removeSdpRecord(recordHandle); + } catch (RemoteException e) { + Log.e(TAG, "removeSdpRecord: ", e); + } + return false; + } + /** * Read the local Out of Band Pairing Data * <p>Requires {@link android.Manifest.permission#BLUETOOTH} @@ -1825,6 +1949,9 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.PAN) { BluetoothPan pan = new BluetoothPan(context, listener); return true; + } else if (profile == BluetoothProfile.DUN) { + BluetoothDun dun = new BluetoothDun(context, listener); + return true; } else if (profile == BluetoothProfile.HEALTH) { BluetoothHealth health = new BluetoothHealth(context, listener); return true; @@ -1837,6 +1964,9 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.SAP) { BluetoothSap sap = new BluetoothSap(context, listener); return true; + } else if (profile == BluetoothProfile.HID_DEVICE) { + BluetoothHidDevice hidd = new BluetoothHidDevice(context, listener); + return true; } else { return false; } @@ -1881,6 +2011,10 @@ public final class BluetoothAdapter { BluetoothPan pan = (BluetoothPan)proxy; pan.close(); break; + case BluetoothProfile.DUN: + BluetoothDun dun = (BluetoothDun)proxy; + dun.close(); + break; case BluetoothProfile.HEALTH: BluetoothHealth health = (BluetoothHealth)proxy; health.close(); @@ -1905,6 +2039,10 @@ public final class BluetoothAdapter { BluetoothSap sap = (BluetoothSap)proxy; sap.close(); break; + case BluetoothProfile.HID_DEVICE: + BluetoothHidDevice hidd = (BluetoothHidDevice) proxy; + hidd.close(); + break; } } @@ -1929,13 +2067,14 @@ public final class BluetoothAdapter { } public void onBluetoothServiceDown() { - if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); + Log.d(TAG, "onBluetoothServiceDown: " + mService); synchronized (mManagerCallback) { mService = null; if (mLeScanClients != null) mLeScanClients.clear(); if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup(); if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup(); synchronized (mProxyServiceStateCallbacks) { + Log.d(TAG, "onBluetoothServiceDown: Sending callbacks to " + mProxyServiceStateCallbacks.size() + " clients"); for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ try { if (cb != null) { @@ -1947,6 +2086,7 @@ public final class BluetoothAdapter { } } } + Log.d(TAG, "onBluetoothServiceDown: Finished sending callbacks to registered clients"); } public void onBrEdrDown() { diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java index b53a8fc..4851087 100644 --- a/core/java/android/bluetooth/BluetoothAvrcpController.java +++ b/core/java/android/bluetooth/BluetoothAvrcpController.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -210,7 +211,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { } public void sendPassThroughCmd(BluetoothDevice device, int keyCode, int keyState) { - if (DBG) Log.d(TAG, "sendPassThroughCmd"); + if (DBG) Log.d(TAG, "sendPassThroughCmd dev = " + device + " key " + keyCode + " State = " + keyState); if (mService != null && isEnabled()) { try { mService.sendPassThroughCmd(device, keyCode, keyState); @@ -223,6 +224,90 @@ public final class BluetoothAvrcpController implements BluetoothProfile { if (mService == null) Log.w(TAG, "Proxy not attached to service"); } + public void getMetaData(int[] attributeIds) { + if (DBG) Log.d(TAG, "getMetaData num requested Ids = " + attributeIds.length); + if (mService != null && isEnabled()) { + try { + mService.getMetaData(attributeIds); + return; + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in getMetaData", e); + return; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + } + + public void getPlayStatus(int[] playStatusIds) { + if (DBG) Log.d(TAG, "getPlayStatus num requested Ids = "+ playStatusIds.length); + if (mService != null && isEnabled()) { + try { + mService.getPlayStatus(playStatusIds); + return; + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in getPlayStatus()", e); + return; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + } + + public void getPlayerApplicationSetting() { + if (DBG) Log.d(TAG, "getPlayerApplicationSetting"); + if (mService != null && isEnabled()) { + try { + mService.getPlayerApplicationSetting(); + return; + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in getPlayerApplicationSetting()", e); + return; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + } + + public void setPlayerApplicationSetting(int attributeId, int attributeVal) { + if (DBG) Log.d(TAG, "setPlayerApplicationSetting attribId = " + attributeId + " attribVal = " + attributeVal); + if (mService != null && isEnabled()) { + try { + mService.setPlayerApplicationSetting(attributeId, attributeVal); + return; + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting()", e); + return; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + } + + public BluetoothAvrcpInfo getSupportedPlayerAppSetting(BluetoothDevice device) { + if (DBG) Log.d(TAG, "getSupportedPlayerAppSetting dev = " + device); + if (mService != null && isEnabled()) { + try { + return mService.getSupportedPlayerAppSetting(device); + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in getSupportedPlayerAppSetting()", e); + return null; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return null; + } + + public int getSupportedFeatures(BluetoothDevice device) { + if (DBG) Log.d(TAG, "getSupportedFeatures dev = " + device); + if (mService != null && isEnabled()) { + try { + return mService.getSupportedFeatures(device); + } catch (RemoteException e) { + Log.e(TAG, "Error talking to BT service in getSupportedFeatures()", e); + return 0; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return 0; + } + private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); diff --git a/core/java/android/bluetooth/BluetoothAvrcpInfo.aidl b/core/java/android/bluetooth/BluetoothAvrcpInfo.aidl new file mode 100644 index 0000000..9b85c80 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothAvrcpInfo.aidl @@ -0,0 +1,34 @@ +/* +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package android.bluetooth; + +parcelable BluetoothAvrcpInfo; diff --git a/core/java/android/bluetooth/BluetoothAvrcpInfo.java b/core/java/android/bluetooth/BluetoothAvrcpInfo.java new file mode 100644 index 0000000..a815d10 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothAvrcpInfo.java @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package android.bluetooth; + +import java.util.ArrayList; +import android.util.Log; + +import android.os.Parcel; +import android.os.Parcelable; +import android.provider.BaseColumns; +import android.net.Uri; + +/** + * Represents the AVRCP Metadata of remote Bluetooth Device. + * + * {@see BluetoothAvrcpController} + * + * {@hide} + */ +public final class BluetoothAvrcpInfo implements Parcelable, BaseColumns{ + + private byte[] supportedPlayerAttributes;// attributes supported + private byte[] numSupportedPlayerAttribValues; // number of values of each attribute + private String TAG = "BluetoothAvrcpInfo"; + /* + * This would a list of values of all AttributeIds + */ + private byte[] supportedPlayerAtribValues; // actual values lies here. + + /* Default Constructor */ + public BluetoothAvrcpInfo() { + supportedPlayerAttributes = null; + numSupportedPlayerAttribValues = null; + supportedPlayerAtribValues = null; + } + public BluetoothAvrcpInfo(byte[] attribIds, byte[] numValueSupported, byte[] valuesSupported) { + int numAttributes = attribIds.length; + int zz = 0; + supportedPlayerAttributes = new byte[numAttributes]; + numSupportedPlayerAttribValues = new byte[numAttributes]; + supportedPlayerAtribValues = new byte[valuesSupported.length]; + for (zz = 0; zz < numAttributes; zz++) { + supportedPlayerAttributes[zz] = attribIds[zz]; + numSupportedPlayerAttribValues[zz] = numValueSupported[zz]; + } + for (zz = 0; zz < supportedPlayerAtribValues.length; zz++) + supportedPlayerAtribValues[zz] = valuesSupported[zz]; + } + /* + * Reading Structure back from Paracel + */ + public BluetoothAvrcpInfo(Parcel source){ + ArrayList<Byte> attribs = new ArrayList<Byte>(); + ArrayList<Byte> numAttribVal = new ArrayList<Byte>(); + ArrayList<Byte> attribVals = new ArrayList<Byte>(); + Byte numAttributes = source.readByte(); + /* + * Read from Source + */ + for(int xx = 0; xx < numAttributes ; xx++) { + attribs.add(source.readByte()); + numAttribVal.add(source.readByte()); + for (int zz = 0; zz < numAttribVal.get(xx); zz++) { + attribVals.add(source.readByte()); + } + } + + /* + * Write Back to Private Data Structures + */ + supportedPlayerAttributes = new byte[attribs.size()]; + for (int zz = 0; zz< attribs.size(); zz++) { + supportedPlayerAttributes[zz] = attribs.get(zz); + } + + numSupportedPlayerAttribValues = new byte[numAttribVal.size()]; + for (int zz = 0; zz< numAttribVal.size(); zz++) { + numSupportedPlayerAttribValues[zz] = numAttribVal.get(zz); + } + + supportedPlayerAtribValues = new byte[attribVals.size()]; + for (int zz = 0; zz< attribVals.size(); zz++) { + supportedPlayerAtribValues[zz] = attribVals.get(zz); + } + } + + public int describeContents() { + return 0; + } + + /* While flatenning the structure we would use the follwing way + * NumAttributes,ID, numValues, Values + */ + public void writeToParcel(Parcel out, int flags) { + byte numSuppAttributes = (byte)supportedPlayerAttributes.length; + out.writeByte(numSuppAttributes); + for (int xx = 0; xx < numSuppAttributes; xx++) { + out.writeByte(supportedPlayerAttributes[xx]); + out.writeByte(numSupportedPlayerAttribValues[xx]); + for (int zz = 0; zz < numSupportedPlayerAttribValues[xx]; zz++) { + out.writeByte(supportedPlayerAtribValues[zz]); + } + } + } + + public byte[] getSupportedPlayerAttributes() { + return supportedPlayerAttributes; + } + + public byte getNumSupportedPlayerAttributeVal(byte playerAttributeId) { + for (int zz = 0; zz < supportedPlayerAttributes.length; zz++) { + if (playerAttributeId == supportedPlayerAttributes[zz]) { + return numSupportedPlayerAttribValues[zz]; + } + } + return 0; + } + + public byte[] getSupportedPlayerAttributeVlaues (byte playerAttributeId) { + int index = 0; + int zz = 0; + boolean attributeFound = false; + for (zz = 0; zz < supportedPlayerAttributes.length; zz++) { + if (playerAttributeId == supportedPlayerAttributes[zz]) { + attributeFound = true; + break; + } + else + index = index + numSupportedPlayerAttribValues[zz]; + } + if (attributeFound) { + byte[] supportedValues = new byte[numSupportedPlayerAttribValues[zz]]; + for (int xx = 0; xx < numSupportedPlayerAttribValues[zz]; xx++) + supportedValues[xx] = supportedPlayerAtribValues[xx + index]; + return supportedValues; + } + else + return new byte[0]; + } + public void putPlayerSettingAttributes(byte[] attribIds, byte[] numValueSupported, byte[] valuesSupported) { + int numAttributes = attribIds.length; + int zz = 0; + supportedPlayerAttributes = new byte[numAttributes]; + numSupportedPlayerAttribValues = new byte[numAttributes]; + supportedPlayerAtribValues = new byte[valuesSupported.length]; + for (zz = 0; zz < numAttributes; zz++) { + supportedPlayerAttributes[zz] = attribIds[zz]; + numSupportedPlayerAttribValues[zz] = numValueSupported[zz]; + } + for (zz = 0; zz < supportedPlayerAtribValues.length; zz++) + supportedPlayerAtribValues[zz] = valuesSupported[zz]; + } + public static final Parcelable.Creator<BluetoothAvrcpInfo> CREATOR = + new Parcelable.Creator<BluetoothAvrcpInfo>() { + public BluetoothAvrcpInfo createFromParcel(Parcel in) { + return new BluetoothAvrcpInfo(in); + } + public BluetoothAvrcpInfo[] newArray(int size) { + return new BluetoothAvrcpInfo[size]; + } + }; + + public static final String PERMISSION_ACCESS = "android.permission.ACCESS_BLUETOOTH_AVRCP_CT_DATA"; + public static final Uri CONTENT_URI = Uri.parse("content://com.android.bluetooth.avrcp/btavrcp_ct"); + + /* + * BaseColumns already has _ID and COUNT values + * Below mentioned strings are used to implement different columns + * of AVRCP MetaData table. + * TRACK_NUM : Ineteger value containing the order number of + * the audio-file on its original recording. + * Numeric ASCII string converted to Integer + * TITLE : Text field representing the title, song name + * ARTIST_NAME : Text field representing artist(s), performer(s) + * ALBUM_NAME : Text field representing the title of the recording + * (source) from which the audio in the file is taken. + * TOTAL_TRACKS : Integet value containing the total number of tracks + * or elements on the original recording. + * GENRE : Text field representing the category of the composition + * characterized by a particular style. + * PLAYING_TIME : Integer containing the length of the audio file in + * milliseconds for eg 02:30 = 150000 + * PLAY_STATUS : Text feild showing current state of track. Possible + * values would be Playing, Stopped, Paused, Forward_Seek + * REV_SEEK + * REPEAT_STATUS : String describing Repeat mode status on remote Media Player + * Posible values "NOT SUPPORTED", "OFF" "Single Track Repeat" + * "All Track Repeat" "Group Repeat" + * SHUFFLE_STATUS : String describing Shuffle mode status on remote Media Player + * Posible values "NOT SUPPORTED", "OFF" "All Track Shuffle" + * "Group Shuffle" + * SCAN_STAUS : String describing SCAN mode status on remote Media Player + * Possible values "NOT SUPPORTED", "OFF","ALL Tracks Scan" + * "Group Scan" + * + * EQUALIZER_STATUS: String describing EQUALIZER mode status on remote Media Player + * Possible values "NOT SUPPORTED", "OFF","ON" + */ + public static final String TRACK_NUM = "track_num"; + public static final String TITLE = "title"; + public static final String ARTIST_NAME = "artist_name"; + public static final String ALBUM_NAME = "album_name"; + public static final String TOTAL_TRACKS = "total_tracks"; + public static final String GENRE = "genre"; + public static final String PLAYING_TIME = "playing_time"; + public static final String TOTAL_TRACK_TIME = "total_track_time"; + public static final String PLAY_STATUS = "play_status"; + public static final String REPEAT_STATUS = "repeat_status"; + public static final String SHUFFLE_STATUS = "shuffle_status"; + public static final String SCAN_STATUS = "scan_status"; + public static final String EQUALIZER_STATUS = "equalizer_status"; + + /* + * Default values for each of the items + */ + public static final int TRACK_NUM_INVALID = 0xFF; + public static final String TITLE_INVALID = "NOT_SUPPORTED"; + public static final String ARTIST_NAME_INVALID = "NOT_SUPPORTED"; + public static final String ALBUM_NAME_INVALID = "NOT_SUPPORTED"; + public static final int TOTAL_TRACKS_INVALID = 0xFF; + public static final String GENRE_INVALID = "NOT_SUPPORTED"; + public static final int PLAYING_TIME_INVALID = 0xFF; + public static final int TOTAL_TRACK_TIME_INVALID = 0xFF; + public static final String PLAY_STATUS_INVALID = "NOT_SUPPORTED"; + public static final String REPEAT_STATUS_INVALID = "NOT_SUPPORTED"; + public static final String SHUFFLE_STATUS_INVALID = "NOT_SUPPORTED"; + public static final String SCAN_STATUS_INVALID = "NOT_SUPPORTED"; + public static final String EQUALIZER_STATUS_INVALID = "NOT_SUPPORTED"; + + /* + *Element Id Values for GetMetaData + */ + public static final int MEDIA_ATTRIBUTE_ALL = 0x00; + public static final int MEDIA_ATTRIBUTE_TITLE = 0x01; + public static final int MEDIA_ATTRIBUTE_ARTIST_NAME = 0x02; + public static final int MEDIA_ATTRIBUTE_ALBUM_NAME = 0x03; + public static final int MEDIA_ATTRIBUTE_TRACK_NUMBER = 0x04; + public static final int MEDIA_ATTRIBUTE_TOTAL_TRACK_NUMBER = 0x05; + public static final int MEDIA_ATTRIBUTE_GENRE = 0x06; + public static final int MEDIA_ATTRIBUTE_PLAYING_TIME = 0x07; + + /* + *PlayStatusId Values for GetPlayStatus + */ + public static final int MEDIA_PLAYSTATUS_ALL = 0x08; + public static final int MEDIA_PLAYSTATUS_SONG_TOTAL_LEN = 0x09; + public static final int MEDIA_PLAYSTATUS_SONG_CUR_POS = 0x0a; + public static final int MEDIA_PLAYSTATUS_SONG_PLAY_STATUS = 0x0b; + + /* + * Values for SetPlayerApplicationSettings + */ + public static final byte ATTRIB_EQUALIZER_STATUS = 0x01; + public static final byte ATTRIB_REPEAT_STATUS = 0x02; + public static final byte ATTRIB_SHUFFLE_STATUS = 0x03; + public static final byte ATTRIB_SCAN_STATUS = 0x04; + + public static final byte EQUALIZER_STATUS_OFF = 0x01; + public static final byte EQUALIZER_STATUS_ON = 0x02; + + public static final byte REPEAT_STATUS_OFF = 0x01; + public static final byte REPEAT_STATUS_SINGLE_TRACK_REPEAT = 0x02; + public static final byte REPEAT_STATUS_ALL_TRACK_REPEAT = 0x03; + public static final byte REPEAT_STATUS_GROUP_REPEAT = 0x04; + + public static final byte SHUFFLE_STATUS_OFF = 0x01; + public static final byte SHUFFLE_STATUS_ALL_TRACK_SHUFFLE = 0x02; + public static final byte SHUFFLE_STATUS_GROUP_SHUFFLE = 0x03; + + public static final byte SCAN_STATUS_OFF = 0x01; + public static final byte SCAN_STATUS_ALL_TRACK_SCAN = 0x02; + public static final byte SCAN_STATUS_GROUP_SCAN = 0x03; + + public static final int BTRC_FEAT_METADATA = 0x01; + public static final int BTRC_FEAT_ABSOLUTE_VOLUME = 0x02; + public static final int BTRC_FEAT_BROWSE = 0x04; + +} diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java index 54bf4af..4a38287 100644..100755 --- a/core/java/android/bluetooth/BluetoothClass.java +++ b/core/java/android/bluetooth/BluetoothClass.java @@ -283,6 +283,8 @@ public final class BluetoothClass implements Parcelable { public static final int PROFILE_PANU = 4; /** @hide */ public static final int PROFILE_NAP = 5; + /** @hide */ + public static final int PROFILE_A2DP_SINK = 6; /** * Check class bits for possible bluetooth profile support. @@ -310,6 +312,21 @@ public final class BluetoothClass implements Parcelable { default: return false; } + } else if (profile == PROFILE_A2DP_SINK) { + if (hasService(Service.CAPTURE)) { + return true; + } + // By the A2DP spec, srcs must indicate the CAPTURE service. + // However if some device that do not, we try to + // match on some other class bits. + switch (getDeviceClass()) { + case Device.AUDIO_VIDEO_HIFI_AUDIO: + case Device.AUDIO_VIDEO_SET_TOP_BOX: + case Device.AUDIO_VIDEO_VCR : + return true; + default: + return false; + } } else if (profile == PROFILE_HEADSET) { // The render service class is required by the spec for HFP, so is a // pretty good signal diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index d27dfa0..b4006de 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -25,6 +25,7 @@ import android.content.Context; import android.os.Parcel; import android.os.Parcelable; import android.os.ParcelUuid; +import android.os.Process; import android.os.RemoteException; import android.util.Log; @@ -823,6 +824,9 @@ public final class BluetoothDevice implements Parcelable { return false; } try { + Log.i(TAG, "createBond() for device " + getAddress() + + " called by pid: " + Process.myPid() + + " tid: " + Process.myTid()); return sService.createBond(this, TRANSPORT_AUTO); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; @@ -854,6 +858,9 @@ public final class BluetoothDevice implements Parcelable { throw new IllegalArgumentException(transport + " is not a valid Bluetooth transport"); } try { + Log.i(TAG, "createBond() for device " + getAddress() + + " called by pid: " + Process.myPid() + + " tid: " + Process.myTid()); return sService.createBond(this, transport); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; @@ -922,6 +929,9 @@ public final class BluetoothDevice implements Parcelable { return false; } try { + Log.i(TAG, "cancelBondProcess() for device " + getAddress() + + " called by pid: " + Process.myPid() + + " tid: " + Process.myTid()); return sService.cancelBondProcess(this); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; @@ -943,6 +953,9 @@ public final class BluetoothDevice implements Parcelable { return false; } try { + Log.i(TAG, "removeBond() for device " + getAddress() + + " called by pid: " + Process.myPid() + + " tid: " + Process.myTid()); return sService.removeBond(this); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; @@ -1390,6 +1403,27 @@ public final class BluetoothDevice implements Parcelable { } /** + * Create an L2cap {@link BluetoothSocket} ready to start an insecure + * outgoing connection to this remote device on given channel. + * <p>The remote device will be not authenticated and communication on this + * socket will not be encrypted. + * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing + * connection. + * <p>Valid L2CAP PSM channels are in range 1 to 2^16. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @param channel L2cap PSM/channel to connect to + * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection + * @throws IOException on error, for example Bluetooth not available, or + * insufficient permissions + * @hide + */ + public BluetoothSocket createInsecureL2capSocket(int channel) throws IOException { + return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, false, false, this, channel, + null); + } + + /** * Create an RFCOMM {@link BluetoothSocket} ready to start a secure * outgoing connection to this remote device using SDP lookup of uuid. * <p>This is designed to be used with {@link diff --git a/core/java/android/bluetooth/BluetoothDevicePicker.java b/core/java/android/bluetooth/BluetoothDevicePicker.java index c794be2..51d14cc 100644 --- a/core/java/android/bluetooth/BluetoothDevicePicker.java +++ b/core/java/android/bluetooth/BluetoothDevicePicker.java @@ -44,6 +44,14 @@ public interface BluetoothDevicePicker { "android.bluetooth.devicepicker.action.DEVICE_SELECTED"; /** + * Broadcast when no BT device is selected from BT device picker screen. + * This happens when user presses back button. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DEVICE_NOT_SELECTED = + "org.codeaurora.bluetooth.devicepicker.action.DEVICE_NOT_SELECTED"; + + /** * Broadcast when someone want to select one BT device from devices list. * This intent contains below extra data: * - {@link #EXTRA_NEED_AUTH} (boolean): if need authentication diff --git a/core/java/android/bluetooth/BluetoothDun.java b/core/java/android/bluetooth/BluetoothDun.java new file mode 100644 index 0000000..0912061 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothDun.java @@ -0,0 +1,296 @@ +/* +*Copyright (c) 2013, The Linux Foundation. All rights reserved. +* +*Redistribution and use in source and binary forms, with or without +*modification, are permitted provided that the following conditions are +*met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided +* with the distribution. +* * Neither the name of The Linux Foundation nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +*THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +*WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +*MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +*ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +*BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +*CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +*SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +*BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +*WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +*OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +*IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +package android.bluetooth; + +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class provides the APIs to control the Bluetooth Dun + * Profile. + * + *<p>BluetoothDun is a proxy object for controlling the Bluetooth DUN + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothDun proxy object. + * + *<p>Each method is protected with its appropriate permission. + *@hide + */ +public final class BluetoothDun implements BluetoothProfile { + private static final String TAG = "BluetoothDun"; + private static final boolean DBG = false; + private static final boolean VDBG = false; + + /** + * Intent used to broadcast the change in connection state of the Dun + * profile. + * + * <p>This intent will have 3 extras: + * <ul> + * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> + * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> + * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> + * </ul> + * + * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTED}. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_CONNECTION_STATE_CHANGED = + "codeaurora.bluetooth.dun.profile.action.CONNECTION_STATE_CHANGED"; + + private Context mContext; + private ServiceListener mServiceListener; + private BluetoothAdapter mAdapter; + private IBluetoothDun mDunService; + + /** + * Create a BluetoothDun proxy object for interacting with the local + * Bluetooth Service which handles the Dun profile + * + */ + /*package*/ BluetoothDun(Context context, ServiceListener l) { + mContext = context; + mServiceListener = l; + mAdapter = BluetoothAdapter.getDefaultAdapter(); + try { + mAdapter.getBluetoothManager().registerStateChangeCallback(mStateChangeCallback); + } catch (RemoteException re) { + Log.w(TAG,"Unable to register BluetoothStateChangeCallback",re); + } + Log.d(TAG, "BluetoothDun() call bindService"); + doBind(); + } + + boolean doBind() { + Intent intent = new Intent(IBluetoothDun.class.getName()); + ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, + android.os.Process.myUserHandle())) { + Log.e(TAG, "Could not bind to Bluetooth Dun Service with " + intent); + return false; + } + return true; + } + + + /*package*/ void close() { + if (VDBG) log("close()"); + mServiceListener = null; + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.unregisterStateChangeCallback(mStateChangeCallback); + } catch (RemoteException re) { + Log.w(TAG,"Unable to unregister BluetoothStateChangeCallback",re); + } + } + + synchronized (mConnection) { + if ( mDunService != null) { + try { + mDunService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } + } + + protected void finalize() { + close(); + } + + private IBluetoothStateChangeCallback mStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + + @Override + public void onBluetoothStateChange(boolean on) { + //Handle enable request to bind again. + Log.d(TAG, "onBluetoothStateChange on: " + on); + if (on) { + try { + if (mDunService == null) { + Log.d(TAG, "onBluetoothStateChange call bindService"); + doBind(); + } + } catch (IllegalStateException e) { + Log.e(TAG,"onBluetoothStateChange: could not bind to DUN service: ", e); + } catch (SecurityException e) { + Log.e(TAG,"onBluetoothStateChange: could not bind to DUN service: ", e); + } + } else { + if (VDBG) Log.d(TAG,"Unbinding service..."); + synchronized (mConnection) { + if ( mDunService != null) { + try { + mDunService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } + } + } + }; + + /** + * Initiate disconnection from DUN server. + * + * <p> Once the disconnection is initiated by any device either local host + * or remote device, the state will transition from {@link #STATE_CONNECTED} + * to {@link #STATE_DISCONNECTED}. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise + * @hide + */ + public boolean disconnect(BluetoothDevice device) { + if (DBG) log("disconnect(" + device + ")"); + if (mDunService != null && isEnabled() && + isValidDevice(device)) { + try { + return mDunService.disconnect(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mDunService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + /** + * {@inheritDoc} + */ + public List<BluetoothDevice> getConnectedDevices() { + if (VDBG) log("getConnectedDevices()"); + if (mDunService != null && isEnabled()) { + try { + return mDunService.getConnectedDevices(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList<BluetoothDevice>(); + } + } + if (mDunService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList<BluetoothDevice>(); + } + + /** + * {@inheritDoc} + */ + public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { + if (VDBG) log("getDevicesMatchingStates()"); + if (mDunService != null && isEnabled()) { + try { + return mDunService.getDevicesMatchingConnectionStates(states); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList<BluetoothDevice>(); + } + } + if (mDunService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList<BluetoothDevice>(); + } + + /** + * {@inheritDoc} + */ + public int getConnectionState(BluetoothDevice device) { + if (VDBG) log("getState(" + device + ")"); + if (mDunService != null && isEnabled() + && isValidDevice(device)) { + try { + return mDunService.getConnectionState(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } + } + if (mDunService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.STATE_DISCONNECTED; + } + + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) Log.d(TAG, "BluetoothDUN Proxy object connected"); + mDunService = IBluetoothDun.Stub.asInterface(service); + + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.DUN, + BluetoothDun.this); + } + } + public void onServiceDisconnected(ComponentName className) { + if (DBG) Log.d(TAG, "BluetoothDUN Proxy object disconnected"); + mDunService = null; + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.DUN); + } + } + }; + + private boolean isEnabled() { + if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; + return false; + } + + private boolean isValidDevice(BluetoothDevice device) { + if (device == null) return false; + + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index 25d9aa9..da81032 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -20,11 +20,11 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.ComponentName; import android.content.Context; -import android.os.Handler; +import android.content.Intent; +import android.content.ServiceConnection; import android.os.IBinder; -import android.os.Looper; -import android.os.Message; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import java.util.ArrayList; @@ -47,7 +47,7 @@ import java.util.List; public final class BluetoothHeadset implements BluetoothProfile { private static final String TAG = "BluetoothHeadset"; private static final boolean DBG = true; - private static final boolean VDBG = false; + private static final boolean VDBG = true; /** * Intent used to broadcast the change in connection state of the Headset @@ -129,6 +129,13 @@ public final class BluetoothHeadset implements BluetoothProfile { "android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT"; /** + * @hide Broadcast intent when HF indicator value changed is updated by HS. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_HF_INDICATOR_VALUE_CHANGED = + "codeaurora.bluetooth.headset.action.ACTION_HF_INDICATOR_VALUE_CHANGED"; + + /** * A String extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} * intents that contains the name of the vendor-specific command. */ @@ -199,6 +206,20 @@ public final class BluetoothHeadset implements BluetoothProfile { public static final String VENDOR_RESULT_CODE_COMMAND_ANDROID = "+ANDROID"; /** + * @hide Used for sharing the HF indicator assigned number. + */ + public static final String HF_INDICATOR_ASSIGNED_NUMBER = + "codeaurora.bluetooth.headset.intent.category.anum"; + + + /** + * @hide Used for sharing the HF indicator assigned number's value. + */ + public static final String HF_INDICATOR_ASSIGNED_NUMBER_VALUE = + "codeaurora.bluetooth.headset.intent.category.anumvalue"; + + + /** * Headset state when SCO audio is not connected. * This state can be one of * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of @@ -222,8 +243,6 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public static final int STATE_AUDIO_CONNECTED = 12; - private static final int MESSAGE_HEADSET_SERVICE_CONNECTED = 100; - private static final int MESSAGE_HEADSET_SERVICE_DISCONNECTED = 101; private Context mContext; private ServiceListener mServiceListener; @@ -236,7 +255,14 @@ public final class BluetoothHeadset implements BluetoothProfile { if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); if (!up) { if (VDBG) Log.d(TAG,"Unbinding service..."); - doUnbind(); + synchronized (mConnection) { + try { + mService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG,"",re); + } + } } else { synchronized (mConnection) { try { @@ -273,26 +299,15 @@ public final class BluetoothHeadset implements BluetoothProfile { } boolean doBind() { - try { - return mAdapter.getBluetoothManager().bindBluetoothProfileService( - BluetoothProfile.HEADSET, mConnection); - } catch (RemoteException e) { - Log.e(TAG, "Unable to bind HeadsetService", e); - } - return false; - } - - void doUnbind() { - synchronized (mConnection) { - if (mService != null) { - try { - mAdapter.getBluetoothManager().unbindBluetoothProfileService( - BluetoothProfile.HEADSET, mConnection); - } catch (RemoteException e) { - Log.e(TAG,"Unable to unbind HeadsetService", e); - } - } + Intent intent = new Intent(IBluetoothHeadset.class.getName()); + ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, + UserHandle.CURRENT_OR_SELF)) { + Log.e(TAG, "Could not bind to Bluetooth Headset Service with " + intent); + return false; } + return true; } /** @@ -312,8 +327,18 @@ public final class BluetoothHeadset implements BluetoothProfile { Log.e(TAG,"",e); } } + + synchronized (mConnection) { + if (mService != null) { + try { + mService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG,"",re); + } + } + } mServiceListener = null; - doUnbind(); } /** @@ -685,6 +710,48 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** + * Sets whether audio routing is allowed. When set to {@code false}, the AG will not route any + * audio to the HF unless explicitly told to. + * This method should be used in cases where the SCO channel is shared between multiple profiles + * and must be delegated by a source knowledgeable + * Note: This is an internal function and shouldn't be exposed + * + * @param allowed {@code true} if the profile can reroute audio, {@code false} otherwise. + * + * @hide + */ + public void setAudioRouteAllowed(boolean allowed) { + if (VDBG) log("setAudioRouteAllowed"); + if (mService != null && isEnabled()) { + try { + mService.setAudioRouteAllowed(allowed); + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + } + } + + /** + * Returns whether audio routing is allowed. see {@link #setAudioRouteAllowed(boolean)}. + * Note: This is an internal function and shouldn't be exposed + * + * @hide + */ + public boolean getAudioRouteAllowed() { + if (VDBG) log("getAudioRouteAllowed"); + if (mService != null && isEnabled()) { + try { + return mService.getAudioRouteAllowed(); + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + } + return false; + } + + /** * Check if Bluetooth SCO audio is connected. * * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. @@ -927,21 +994,21 @@ public final class BluetoothHeadset implements BluetoothProfile { return false; } - private final IBluetoothProfileServiceConnection mConnection - = new IBluetoothProfileServiceConnection.Stub() { - @Override + private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { if (DBG) Log.d(TAG, "Proxy object connected"); mService = IBluetoothHeadset.Stub.asInterface(service); - mHandler.sendMessage(mHandler.obtainMessage( - MESSAGE_HEADSET_SERVICE_CONNECTED)); + + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.HEADSET, BluetoothHeadset.this); + } } - @Override public void onServiceDisconnected(ComponentName className) { if (DBG) Log.d(TAG, "Proxy object disconnected"); mService = null; - mHandler.sendMessage(mHandler.obtainMessage( - MESSAGE_HEADSET_SERVICE_DISCONNECTED)); + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET); + } } }; @@ -965,25 +1032,4 @@ public final class BluetoothHeadset implements BluetoothProfile { private static void log(String msg) { Log.d(TAG, msg); } - - private final Handler mHandler = new Handler(Looper.getMainLooper()) { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MESSAGE_HEADSET_SERVICE_CONNECTED: { - if (mServiceListener != null) { - mServiceListener.onServiceConnected(BluetoothProfile.HEADSET, - BluetoothHeadset.this); - } - break; - } - case MESSAGE_HEADSET_SERVICE_DISCONNECTED: { - if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET); - } - break; - } - } - } - }; } diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java index ff4ebee..484a856 100644 --- a/core/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java @@ -100,7 +100,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * {@link #EXTRA_BATTERY_LEVEL}, * {@link #EXTRA_OPERATOR_NAME}, * {@link #EXTRA_VOICE_RECOGNITION}, - * {@link #EXTRA_IN_BAND_RING}</p> + * {@link #EXTRA_IN_BAND_RING} + * {@link #EXTRA_MANF_ID} + * {@link #EXTRA_MANF_MODEL}</p> */ public static final String ACTION_AG_EVENT = "android.bluetooth.headsetclient.profile.action.AG_EVENT"; @@ -206,6 +208,21 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { "android.bluetooth.headsetclient.extra.SUBSCRIBER_INFO"; /** + * Extra for AG_EVENT intent indicates manufacturer identification. + * <p>Value: <code>String</code> containing manufacturer identification.</p> + */ + public static final String EXTRA_MANF_ID = + "android.bluetooth.headsetclient.extra.MANF_ID"; + + /** + * Extra for AG_EVENT intent indicates manufacturer model. + * <p>Value: <code>String</code> containing manufacturer model.</p> + */ + public static final String EXTRA_MANF_MODEL = + "android.bluetooth.headsetclient.extra.MANF_MODEL"; + + + /** * Extra for AG_CALL_CHANGED intent indicates the * {@link BluetoothHeadsetClientCall} object that has changed. */ @@ -1059,6 +1076,41 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { } /** + * Sets whether audio routing is allowed. + * + * Note: This is an internal function and shouldn't be exposed + */ + public void setAudioRouteAllowed(boolean allowed) { + if (VDBG) log("setAudioRouteAllowed"); + if (mService != null && isEnabled()) { + try { + mService.setAudioRouteAllowed(allowed); + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + } + } + + /** + * Returns whether audio routing is allowed. + * + * Note: This is an internal function and shouldn't be exposed + */ + public boolean getAudioRouteAllowed() { + if (VDBG) log("getAudioRouteAllowed"); + if (mService != null && isEnabled()) { + try { + return mService.getAudioRouteAllowed(); + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + } + return false; + } + + /** * Initiates a connection of audio channel. * * It setup SCO channel with remote connected Handsfree AG device. diff --git a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java index 7b5a045..002f63f 100644 --- a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -19,6 +19,8 @@ package android.bluetooth; import android.os.Parcel; import android.os.Parcelable; +import java.util.UUID; + /** * This class represents a single call, its state and properties. * It implements {@link Parcelable} for inter-process message passing. @@ -67,14 +69,21 @@ public final class BluetoothHeadsetClientCall implements Parcelable { private String mNumber; private boolean mMultiParty; private final boolean mOutgoing; + private final UUID mUUID; /** * Creates BluetoothHeadsetClientCall instance. */ public BluetoothHeadsetClientCall(BluetoothDevice device, int id, int state, String number, boolean multiParty, boolean outgoing) { + this(device, id, UUID.randomUUID(), state, number, multiParty, outgoing); + } + + public BluetoothHeadsetClientCall(BluetoothDevice device, int id, UUID uuid, int state, + String number, boolean multiParty, boolean outgoing) { mDevice = device; mId = id; + mUUID = uuid; mState = state; mNumber = number != null ? number : ""; mMultiParty = multiParty; @@ -134,6 +143,16 @@ public final class BluetoothHeadsetClientCall implements Parcelable { } /** + * Gets call's UUID. + * + * @return call uuid + * @hide + */ + public UUID getUUID() { + return mUUID; + } + + /** * Gets call's current state. * * @return state of this particular phone call. @@ -172,10 +191,16 @@ public final class BluetoothHeadsetClientCall implements Parcelable { } public String toString() { + return toString(false); + } + + public String toString(boolean loggable) { StringBuilder builder = new StringBuilder("BluetoothHeadsetClientCall{mDevice: "); - builder.append(mDevice); + builder.append(loggable ? mDevice.hashCode() : mDevice); builder.append(", mId: "); builder.append(mId); + builder.append(", mUUID: "); + builder.append(mUUID); builder.append(", mState: "); switch (mState) { case CALL_STATE_ACTIVE: builder.append("ACTIVE"); break; @@ -189,7 +214,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { default: builder.append(mState); break; } builder.append(", mNumber: "); - builder.append(mNumber); + builder.append(loggable ? mNumber.hashCode() : mNumber); builder.append(", mMultiParty: "); builder.append(mMultiParty); builder.append(", mOutgoing: "); @@ -206,8 +231,8 @@ public final class BluetoothHeadsetClientCall implements Parcelable { @Override public BluetoothHeadsetClientCall createFromParcel(Parcel in) { return new BluetoothHeadsetClientCall((BluetoothDevice)in.readParcelable(null), - in.readInt(), in.readInt(), in.readString(), - in.readInt() == 1, in.readInt() == 1); + in.readInt(), UUID.fromString(in.readString()), in.readInt(), + in.readString(), in.readInt() == 1, in.readInt() == 1); } @Override @@ -220,6 +245,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeParcelable(mDevice, 0); out.writeInt(mId); + out.writeString(mUUID.toString()); out.writeInt(mState); out.writeString(mNumber); out.writeInt(mMultiParty ? 1 : 0); diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java new file mode 100644 index 0000000..468df4d --- /dev/null +++ b/core/java/android/bluetooth/BluetoothHidDevice.java @@ -0,0 +1,502 @@ +/* + * Copyright (C) 2013 The Linux Foundation. All rights reserved + * Not a Contribution. + * Copyright (C) 2011 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.bluetooth; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import java.util.Arrays; +import java.util.List; + +/** + * @hide + */ +public final class BluetoothHidDevice implements BluetoothProfile { + + private static final String TAG = BluetoothHidDevice.class.getSimpleName(); + + public static final String ACTION_CONNECTION_STATE_CHANGED = + "codeaurora.bluetooth.hid.profile.action.CONNECTION_STATE_CHANGED"; + + /** + * Constants representing device subclass. + * + * @see #registerApp(String, String, String, byte, byte[], + * BluetoothHidDeviceCallback) + */ + public static final byte SUBCLASS1_NONE = (byte) 0x00; + public static final byte SUBCLASS1_KEYBOARD = (byte) 0x40; + public static final byte SUBCLASS1_MOUSE = (byte) 0x80; + public static final byte SUBCLASS1_COMBO = (byte) 0xC0; + + public static final byte SUBCLASS2_UNCATEGORIZED = (byte) 0x00; + public static final byte SUBCLASS2_JOYSTICK = (byte) 0x01; + public static final byte SUBCLASS2_GAMEPAD = (byte) 0x02; + public static final byte SUBCLASS2_REMOTE_CONTROL = (byte) 0x03; + public static final byte SUBCLASS2_SENSING_DEVICE = (byte) 0x04; + public static final byte SUBCLASS2_DIGITIZER_TABLED = (byte) 0x05; + public static final byte SUBCLASS2_CARD_READER = (byte) 0x06; + + /** + * Constants representing report types. + * + * @see BluetoothHidDeviceCallback#onGetReport(byte, byte, int) + * @see BluetoothHidDeviceCallback#onSetReport(byte, byte, byte[]) + * @see BluetoothHidDeviceCallback#onIntrData(byte, byte[]) + */ + public static final byte REPORT_TYPE_INPUT = (byte) 1; + public static final byte REPORT_TYPE_OUTPUT = (byte) 2; + public static final byte REPORT_TYPE_FEATURE = (byte) 3; + + /** + * Constants representing error response for Set Report. + * + * @see BluetoothHidDeviceCallback#onSetReport(byte, byte, byte[]) + */ + public static final byte ERROR_RSP_SUCCESS = (byte) 0; + public static final byte ERROR_RSP_NOT_READY = (byte) 1; + public static final byte ERROR_RSP_INVALID_RPT_ID = (byte) 2; + public static final byte ERROR_RSP_UNSUPPORTED_REQ = (byte) 3; + public static final byte ERROR_RSP_INVALID_PARAM = (byte) 4; + public static final byte ERROR_RSP_UNKNOWN = (byte) 14; + + /** + * Constants representing protocol mode used set by host. Default is always + * {@link #PROTOCOL_REPORT_MODE} unless notified otherwise. + * + * @see BluetoothHidDeviceCallback#onSetProtocol(byte) + */ + public static final byte PROTOCOL_BOOT_MODE = (byte) 0; + public static final byte PROTOCOL_REPORT_MODE = (byte) 1; + + private Context mContext; + + private ServiceListener mServiceListener; + + private IBluetoothHidDevice mService; + + private BluetoothAdapter mAdapter; + + private static class BluetoothHidDeviceCallbackWrapper extends IBluetoothHidDeviceCallback.Stub { + + private BluetoothHidDeviceCallback mCallback; + + public BluetoothHidDeviceCallbackWrapper(BluetoothHidDeviceCallback callback) { + mCallback = callback; + } + + @Override + public void onAppStatusChanged(BluetoothDevice pluggedDevice, + BluetoothHidDeviceAppConfiguration config, boolean registered) { + mCallback.onAppStatusChanged(pluggedDevice, config, registered); + } + + @Override + public void onConnectionStateChanged(BluetoothDevice device, int state) { + mCallback.onConnectionStateChanged(device, state); + } + + @Override + public void onGetReport(byte type, byte id, int bufferSize) { + mCallback.onGetReport(type, id, bufferSize); + } + + @Override + public void onSetReport(byte type, byte id, byte[] data) { + mCallback.onSetReport(type, id, data); + } + + @Override + public void onSetProtocol(byte protocol) { + mCallback.onSetProtocol(protocol); + } + + @Override + public void onIntrData(byte reportId, byte[] data) { + mCallback.onIntrData(reportId, data); + } + + @Override + public void onVirtualCableUnplug() { + mCallback.onVirtualCableUnplug(); + } + } + + final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + + public void onBluetoothStateChange(boolean up) { + Log.d(TAG, "onBluetoothStateChange: up=" + up); + synchronized (mConnection) { + if (!up) { + Log.d(TAG,"Unbinding service..."); + if (mService != null) { + mService = null; + try { + mContext.unbindService(mConnection); + } catch (IllegalArgumentException e) { + Log.e(TAG,"onBluetoothStateChange: could not unbind service:", e); + } + } + } else { + try { + if (mService == null) { + Log.d(TAG,"Binding HID Device service..."); + doBind(); + } + } catch (IllegalStateException e) { + Log.e(TAG,"onBluetoothStateChange: could not bind to HID Dev service: ", e); + } catch (SecurityException e) { + Log.e(TAG,"onBluetoothStateChange: could not bind to HID Dev service: ", e); + } + } + } + } + }; + + private ServiceConnection mConnection = new ServiceConnection() { + + public void onServiceConnected(ComponentName className, IBinder service) { + Log.d(TAG, "onServiceConnected()"); + + mService = IBluetoothHidDevice.Stub.asInterface(service); + + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.HID_DEVICE, + BluetoothHidDevice.this); + } + } + + public void onServiceDisconnected(ComponentName className) { + Log.d(TAG, "onServiceDisconnected()"); + + mService = null; + + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.HID_DEVICE); + } + } + }; + + BluetoothHidDevice(Context context, ServiceListener listener) { + Log.v(TAG, "BluetoothHidDevice"); + + mContext = context; + mServiceListener = listener; + mAdapter = BluetoothAdapter.getDefaultAdapter(); + + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + doBind(); + } + + boolean doBind() { + Intent intent = new Intent(IBluetoothHidDevice.class.getName()); + ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, + android.os.Process.myUserHandle())) { + Log.e(TAG, "Could not bind to Bluetooth HID Device Service with " + intent); + return false; + } + Log.d(TAG, "Bound to HID Device Service"); + return true; + } + + void close() { + Log.v(TAG, "close()"); + + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + synchronized (mConnection) { + if (mService != null) { + mService = null; + try { + mContext.unbindService(mConnection); + } catch (IllegalArgumentException e) { + Log.e(TAG,"close: could not unbind HID Dev service: ", e); + } + } + } + + mServiceListener = null; + } + + @Override + public List<BluetoothDevice> getConnectedDevices() { + Log.v(TAG, "getConnectedDevices()"); + return null; + } + + @Override + public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { + Log.v(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states)); + return null; + } + + @Override + public int getConnectionState(BluetoothDevice device) { + Log.v(TAG, "getConnectionState(): device=" + device.getAddress()); + + return STATE_DISCONNECTED; + } + + /** + * Registers application to be used for HID device. Connections to HID + * Device are only possible when application is registered. Only one + * application can be registered at time. When no longer used, application + * should be unregistered using + * {@link #unregisterApp(BluetoothHidDeviceAppConfiguration)}. + * + * @param sdp {@link BluetoothHidDeviceAppSdpSettings} object of + * HID Device SDP record. + * @param inQos {@link BluetoothHidDeviceAppQosSettings} object of + * Incoming QoS Settings. + * @param outQos {@link BluetoothHidDeviceAppQosSettings} object of + * Outgoing QoS Settings. + * @param callback {@link BluetoothHidDeviceCallback} object to which + * callback messages will be sent. + * @return + */ + public boolean registerApp(BluetoothHidDeviceAppSdpSettings sdp, + BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos, + BluetoothHidDeviceCallback callback) { + Log.v(TAG, "registerApp(): sdp=" + sdp + " inQos=" + inQos + " outQos=" + outQos + + " callback=" + callback); + + boolean result = false; + + if (sdp == null || callback == null) { + return false; + } + + if (mService != null) { + try { + BluetoothHidDeviceAppConfiguration config = + new BluetoothHidDeviceAppConfiguration(); + BluetoothHidDeviceCallbackWrapper cbw = + new BluetoothHidDeviceCallbackWrapper(callback); + result = mService.registerApp(config, sdp, inQos, outQos, cbw); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return result; + } + + /** + * Unregisters application. Active connection will be disconnected and no + * new connections will be allowed until registered again using + * {@link #registerApp(String, String, String, byte, byte[], BluetoothHidDeviceCallback)} + * + * @param config {@link BluetoothHidDeviceAppConfiguration} object as + * obtained from + * {@link BluetoothHidDeviceCallback#onAppStatusChanged(BluetoothDevice, + * BluetoothHidDeviceAppConfiguration, boolean)} + * + * @return + */ + public boolean unregisterApp(BluetoothHidDeviceAppConfiguration config) { + Log.v(TAG, "unregisterApp()"); + + boolean result = false; + + if (mService != null) { + try { + result = mService.unregisterApp(config); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return result; + } + + /** + * Sends report to remote host using interrupt channel. + * + * @param id Report Id, as defined in descriptor. Can be 0 in case Report Id + * are not defined in descriptor. + * @param data Report data, not including Report Id. + * @return + */ + public boolean sendReport(int id, byte[] data) { + Log.v(TAG, "sendReport(): id=" + id); + + boolean result = false; + + if (mService != null) { + try { + result = mService.sendReport(id, data); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return result; + } + + /** + * Sends report to remote host as reply for GET_REPORT request from + * {@link BluetoothHidDeviceCallback#onGetReport(byte, byte, int)}. + * + * @param type Report Type, as in request. + * @param id Report Id, as in request. + * @param data Report data, not including Report Id. + * @return + */ + public boolean replyReport(byte type, byte id, byte[] data) { + Log.v(TAG, "replyReport(): type=" + type + " id=" + id); + + boolean result = false; + + if (mService != null) { + try { + result = mService.replyReport(type, id, data); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return result; + } + + /** + * Sends error handshake message as reply for invalid SET_REPORT request + * from {@link BluetoothHidDeviceCallback#onSetReport(byte, byte, byte[])}. + * + * @param error Error to be sent for SET_REPORT via HANDSHAKE. + * @return + */ + public boolean reportError(byte error) { + Log.v(TAG, "reportError(): error = " + error); + + boolean result = false; + + if (mService != null) { + try { + result = mService.reportError(error); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return result; + } + + /** + * Sends Virtual Cable Unplug to currently connected host. + * + * @return + */ + public boolean unplug() { + Log.v(TAG, "unplug()"); + + boolean result = false; + + if (mService != null) { + try { + result = mService.unplug(); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return result; + } + + /** + * Initiates connection to host which currently has Virtual Cable + * established with device. + * + * @return + */ + public boolean connect() { + Log.v(TAG, "connect()"); + + boolean result = false; + + if (mService != null) { + try { + result = mService.connect(); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return result; + } + + /** + * Disconnects from currently connected host. + * + * @return + */ + public boolean disconnect() { + Log.v(TAG, "disconnect()"); + + boolean result = false; + + if (mService != null) { + try { + result = mService.disconnect(); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + } + + return result; + } +} diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl b/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl new file mode 100644 index 0000000..1af309c --- /dev/null +++ b/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.aidl @@ -0,0 +1,21 @@ +/* +** Copyright (C) 2013 The Linux Foundation. All rights reserved +** Not a Contribution. +** Copyright 2011, 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.bluetooth; + +parcelable BluetoothHidDeviceAppConfiguration; diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java b/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java new file mode 100644 index 0000000..9f3cd3c --- /dev/null +++ b/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2013 The Linux Foundation. All rights reserved + * Not a Contribution. + * Copyright (C) 2011 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.bluetooth; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Random; + +/** @hide */ +public final class BluetoothHidDeviceAppConfiguration implements Parcelable { + private final long mHash; + + BluetoothHidDeviceAppConfiguration() { + Random rnd = new Random(); + mHash = rnd.nextLong(); + } + + BluetoothHidDeviceAppConfiguration(long hash) { + mHash = hash; + } + + @Override + public boolean equals(Object o) { + if (o instanceof BluetoothHidDeviceAppConfiguration) { + BluetoothHidDeviceAppConfiguration config = (BluetoothHidDeviceAppConfiguration) o; + return mHash == config.mHash; + } + return false; + } + + @Override + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator<BluetoothHidDeviceAppConfiguration> CREATOR = + new Parcelable.Creator<BluetoothHidDeviceAppConfiguration>() { + + @Override + public BluetoothHidDeviceAppConfiguration createFromParcel(Parcel in) { + long hash = in.readLong(); + return new BluetoothHidDeviceAppConfiguration(hash); + } + + @Override + public BluetoothHidDeviceAppConfiguration[] newArray(int size) { + return new BluetoothHidDeviceAppConfiguration[size]; + } + }; + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeLong(mHash); + } +} diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl new file mode 100644 index 0000000..ae93235 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl @@ -0,0 +1,21 @@ +/* +** Copyright (C) 2013 The Linux Foundation. All rights reserved +** Not a Contribution. +** Copyright 2011, 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.bluetooth; + +parcelable BluetoothHidDeviceAppQosSettings; diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java new file mode 100644 index 0000000..a4044d9 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2013 The Linux Foundation. All rights reserved + * Not a Contribution. + * Copyright (C) 2011 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.bluetooth; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Random; + +/** @hide */ +public final class BluetoothHidDeviceAppQosSettings implements Parcelable { + + final public int serviceType; + final public int tokenRate; + final public int tokenBucketSize; + final public int peakBandwidth; + final public int latency; + final public int delayVariation; + + final static public int SERVICE_NO_TRAFFIC = 0x00; + final static public int SERVICE_BEST_EFFORT = 0x01; + final static public int SERVICE_GUARANTEED = 0x02; + + final static public int MAX = (int) 0xffffffff; + + public BluetoothHidDeviceAppQosSettings(int serviceType, int tokenRate, int tokenBucketSize, + int peakBandwidth, + int latency, int delayVariation) { + this.serviceType = serviceType; + this.tokenRate = tokenRate; + this.tokenBucketSize = tokenBucketSize; + this.peakBandwidth = peakBandwidth; + this.latency = latency; + this.delayVariation = delayVariation; + } + + @Override + public boolean equals(Object o) { + if (o instanceof BluetoothHidDeviceAppQosSettings) { + BluetoothHidDeviceAppQosSettings qos = (BluetoothHidDeviceAppQosSettings) o; + return false; + } + return false; + } + + @Override + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator<BluetoothHidDeviceAppQosSettings> CREATOR = + new Parcelable.Creator<BluetoothHidDeviceAppQosSettings>() { + + @Override + public BluetoothHidDeviceAppQosSettings createFromParcel(Parcel in) { + + return new BluetoothHidDeviceAppQosSettings(in.readInt(), in.readInt(), in.readInt(), + in.readInt(), + in.readInt(), in.readInt()); + } + + @Override + public BluetoothHidDeviceAppQosSettings[] newArray(int size) { + return new BluetoothHidDeviceAppQosSettings[size]; + } + }; + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(serviceType); + out.writeInt(tokenRate); + out.writeInt(tokenBucketSize); + out.writeInt(peakBandwidth); + out.writeInt(latency); + out.writeInt(delayVariation); + } + + public int[] toArray() { + return new int[] { + serviceType, tokenRate, tokenBucketSize, peakBandwidth, latency, delayVariation + }; + } +} diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl new file mode 100644 index 0000000..38ac1ec --- /dev/null +++ b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl @@ -0,0 +1,21 @@ +/* +** Copyright (C) 2013 The Linux Foundation. All rights reserved +** Not a Contribution. +** Copyright 2011, 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.bluetooth; + +parcelable BluetoothHidDeviceAppSdpSettings; diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java new file mode 100644 index 0000000..db88f0d --- /dev/null +++ b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2013 The Linux Foundation. All rights reserved + * Not a Contribution. + * Copyright (C) 2011 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.bluetooth; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Random; + +/** @hide */ +public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { + + final public String name; + final public String description; + final public String provider; + final public byte subclass; + final public byte[] descriptors; + + public BluetoothHidDeviceAppSdpSettings(String name, String description, String provider, + byte subclass, byte[] descriptors) { + this.name = name; + this.description = description; + this.provider = provider; + this.subclass = subclass; + this.descriptors = descriptors.clone(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof BluetoothHidDeviceAppSdpSettings) { + BluetoothHidDeviceAppSdpSettings sdp = (BluetoothHidDeviceAppSdpSettings) o; + return false; + } + return false; + } + + @Override + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator<BluetoothHidDeviceAppSdpSettings> CREATOR = + new Parcelable.Creator<BluetoothHidDeviceAppSdpSettings>() { + + @Override + public BluetoothHidDeviceAppSdpSettings createFromParcel(Parcel in) { + + return new BluetoothHidDeviceAppSdpSettings(in.readString(), in.readString(), + in.readString(), in.readByte(), in.createByteArray()); + } + + @Override + public BluetoothHidDeviceAppSdpSettings[] newArray(int size) { + return new BluetoothHidDeviceAppSdpSettings[size]; + } + }; + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeString(name); + out.writeString(description); + out.writeString(provider); + out.writeByte(subclass); + out.writeByteArray(descriptors); + } +} diff --git a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java new file mode 100644 index 0000000..cc60833 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2013 The Linux Foundation. All rights reserved + * Not a Contribution. + * Copyright (C) 2011 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.bluetooth; + +import android.util.Log; + +/** @hide */ +public abstract class BluetoothHidDeviceCallback { + + private static final String TAG = BluetoothHidDeviceCallback.class.getSimpleName(); + + /** + * Callback called when application registration state changes. Usually it's + * called due to either + * {@link BluetoothHidDevice#registerApp(String, String, String, byte, byte[], + * BluetoothHidDeviceCallback)} + * or + * {@link BluetoothHidDevice#unregisterApp(BluetoothHidDeviceAppConfiguration)} + * , but can be also unsolicited in case e.g. Bluetooth was turned off in + * which case application is unregistered automatically. + * + * @param pluggedDevice {@link BluetoothDevice} object which represents host + * that currently has Virtual Cable established with device. Only + * valid when application is registered, can be <code>null</code> + * . + * @param config {@link BluetoothHidDeviceAppConfiguration} object which + * represents token required to unregister application using + * {@link BluetoothHidDevice#unregisterApp(BluetoothHidDeviceAppConfiguration)} + * . + * @param registered <code>true</code> if application is registered, + * <code>false</code> otherwise. + */ + public void onAppStatusChanged(BluetoothDevice pluggedDevice, + BluetoothHidDeviceAppConfiguration config, boolean registered) { + Log.d(TAG, "onAppStatusChanged: pluggedDevice=" + (pluggedDevice == null ? + null : pluggedDevice.toString()) + " registered=" + registered); + } + + /** + * Callback called when connection state with remote host was changed. + * Application can assume than Virtual Cable is established when called with + * {@link BluetoothProfile#STATE_CONNECTED} <code>state</code>. + * + * @param device {@link BluetoothDevice} object representing host device + * which connection state was changed. + * @param state Connection state as defined in {@link BluetoothProfile}. + */ + public void onConnectionStateChanged(BluetoothDevice device, int state) { + Log.d(TAG, "onConnectionStateChanged: device=" + device.toString() + " state=" + state); + } + + /** + * Callback called when GET_REPORT is received from remote host. Should be + * replied by application using + * {@link BluetoothHidDevice#replyReport(byte, byte, byte[])}. + * + * @param type Requested Report Type. + * @param id Requested Report Id, can be 0 if no Report Id are defined in + * descriptor. + * @param bufferSize Requested buffer size, application shall respond with + * at least given number of bytes. + */ + public void onGetReport(byte type, byte id, int bufferSize) { + Log.d(TAG, "onGetReport: type=" + type + " id=" + id + " bufferSize=" + bufferSize); + } + + /** + * Callback called when SET_REPORT is received from remote host. In case + * received data are invalid, application shall respond with + * {@link BluetoothHidDevice#reportError()}. + * + * @param type Report Type. + * @param id Report Id. + * @param data Report data. + */ + public void onSetReport(byte type, byte id, byte[] data) { + Log.d(TAG, "onSetReport: type=" + type + " id=" + id); + } + + /** + * Callback called when SET_PROTOCOL is received from remote host. + * Application shall use this information to send only reports valid for + * given protocol mode. By default, + * {@link BluetoothHidDevice#PROTOCOL_REPORT_MODE} shall be assumed. + * + * @param protocol Protocol Mode. + */ + public void onSetProtocol(byte protocol) { + Log.d(TAG, "onSetProtocol: protocol=" + protocol); + } + + /** + * Callback called when report data is received over interrupt channel. + * Report Type is assumed to be + * {@link BluetoothHidDevice#REPORT_TYPE_OUTPUT}. + * + * @param reportId Report Id. + * @param data Report data. + */ + public void onIntrData(byte reportId, byte[] data) { + Log.d(TAG, "onIntrData: reportId=" + reportId); + } + + /** + * Callback called when Virtual Cable is removed. This can be either due to + * {@link BluetoothHidDevice#unplug()} or request from remote side. After + * this callback is received connection will be disconnected automatically. + */ + public void onVirtualCableUnplug() { + Log.d(TAG, "onVirtualCableUnplug"); + } +} diff --git a/core/java/android/bluetooth/BluetoothInputDevice.java b/core/java/android/bluetooth/BluetoothInputDevice.java index 252e3d2..db23ef5 100644 --- a/core/java/android/bluetooth/BluetoothInputDevice.java +++ b/core/java/android/bluetooth/BluetoothInputDevice.java @@ -96,6 +96,12 @@ public final class BluetoothInputDevice implements BluetoothProfile { public static final String ACTION_VIRTUAL_UNPLUG_STATUS = "android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS"; + /** + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_IDLE_TIME_CHANGED = + "codeaurora.bluetooth.input.profile.action.IDLE_TIME_CHANGED"; /** * Return codes for the connect and disconnect Bluez / Dbus calls. @@ -199,6 +205,11 @@ public final class BluetoothInputDevice implements BluetoothProfile { */ public static final String EXTRA_VIRTUAL_UNPLUG_STATUS = "android.bluetooth.BluetoothInputDevice.extra.VIRTUAL_UNPLUG_STATUS"; + /** + * @hide + */ + public static final String EXTRA_IDLE_TIME = "codeaurora.bluetooth.BluetoothInputDevice.extra.IDLE_TIME"; + private Context mContext; private ServiceListener mServiceListener; private BluetoothAdapter mAdapter; @@ -658,6 +669,56 @@ public final class BluetoothInputDevice implements BluetoothProfile { if (mService == null) Log.w(TAG, "Proxy not attached to service"); return false; } + + /** + * Send Get_Idle_Time command to the connected HID input device. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise + * @hide + */ + public boolean getIdleTime(BluetoothDevice device) { + if (DBG) log("getIdletime(" + device + ")"); + if (mService != null && isEnabled() && isValidDevice(device)) { + try { + return mService.getIdleTime(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Send Set_Idle_Time command to the connected HID input device. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. + * + * @param device Remote Bluetooth Device + * @param idleTime Idle time to be set on HID Device + * @return false on immediate error, + * true otherwise + * @hide + */ + public boolean setIdleTime(BluetoothDevice device, byte idleTime) { + if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime); + if (mService != null && isEnabled() && isValidDevice(device)) { + try { + return mService.setIdleTime(device, idleTime); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + private static void log(String msg) { Log.d(TAG, msg); } diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index cbce22c..9ef931e 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -131,6 +131,18 @@ public interface BluetoothProfile { public static final int HEADSET_CLIENT = 16; /** + * HID device + * @hide + */ + public static final int HID_DEVICE = 17; + + /** + * DUN + * @hide + */ + public static final int DUN = 21; + + /** * Default priority for devices that we try to auto-connect to and * and allow incoming connections for the profile * @hide diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java index fb81fd1..2eb4953 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -247,6 +247,7 @@ public final class BluetoothSocket implements Closeable { as.mSocketOS = as.mSocket.getOutputStream(); as.mAddress = RemoteAddr; as.mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(RemoteAddr); + as.mPort = mPort; return as; } /** @@ -468,6 +469,61 @@ public final class BluetoothSocket implements Closeable { return acceptedSocket; } + /** + * setSocketOpt for the Buetooth Socket. + * + * @param optionName socket option name + * @param optionVal socket option value + * @param optionLen socket option length + * @return -1 on immediate error, + * 0 otherwise + * @hide + */ + public int setSocketOpt(int optionName, byte [] optionVal, int optionLen) throws IOException { + int ret = 0; + if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); + IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); + if (bluetoothProxy == null) { + Log.e(TAG, "setSocketOpt fail, reason: bluetooth is off"); + return -1; + } + try { + if(VDBG) Log.d(TAG, "setSocketOpt(), mType: " + mType + " mPort: " + mPort); + ret = bluetoothProxy.setSocketOpt(mType, mPort, optionName, optionVal, optionLen); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return -1; + } + return ret; + } + + /** + * getSocketOpt for the Buetooth Socket. + * + * @param optionName socket option name + * @param optionVal socket option value + * @return -1 on immediate error, + * length of returned socket option otherwise + * @hide + */ + public int getSocketOpt(int optionName, byte [] optionVal) throws IOException { + int ret = 0; + if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); + IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); + if (bluetoothProxy == null) { + Log.e(TAG, "getSocketOpt fail, reason: bluetooth is off"); + return -1; + } + try { + if(VDBG) Log.d(TAG, "getSocketOpt(), mType: " + mType + " mPort: " + mPort); + ret = bluetoothProxy.getSocketOpt(mType, mPort, optionName, optionVal); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return -1; + } + return ret; + } + /*package*/ int available() throws IOException { if (VDBG) Log.d(TAG, "available: " + mSocketIS); return mSocketIS.available(); diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index 66f3418..2f1e8b4 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -70,6 +70,11 @@ interface IBluetooth boolean fetchRemoteUuids(in BluetoothDevice device); boolean sdpSearch(in BluetoothDevice device, in ParcelUuid uuid); + int createMapMnsSdpRecord(in String serviceName, in int rfcommChannel, + in int l2capPsm, in int version, in int features); + int createPbapPceSdpRecord(in String serviceName, in int version); + boolean removeSdpRecord(in int recordHandle); + boolean setPin(in BluetoothDevice device, boolean accept, int len, in byte[] pinCode); boolean setPasskey(in BluetoothDevice device, boolean accept, int len, in byte[] passkey); @@ -106,4 +111,7 @@ interface IBluetooth void dump(in ParcelFileDescriptor fd); void onLeServiceUp(); void onBrEdrDown(); + + int setSocketOpt(int type, int port, int optionName, in byte [] optionVal, int optionLen); + int getSocketOpt(int type, int port, int optionName, out byte [] optionVal); } diff --git a/core/java/android/bluetooth/IBluetoothA2dpSink.aidl b/core/java/android/bluetooth/IBluetoothA2dpSink.aidl index b7c6476..774a1ec 100644..100755 --- a/core/java/android/bluetooth/IBluetoothA2dpSink.aidl +++ b/core/java/android/bluetooth/IBluetoothA2dpSink.aidl @@ -30,5 +30,8 @@ interface IBluetoothA2dpSink { List<BluetoothDevice> getConnectedDevices(); List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states); int getConnectionState(in BluetoothDevice device); + boolean setPriority(in BluetoothDevice device, int priority); + int getPriority(in BluetoothDevice device); + boolean isA2dpPlaying(in BluetoothDevice device); BluetoothAudioConfig getAudioConfig(in BluetoothDevice device); } diff --git a/core/java/android/bluetooth/IBluetoothAvrcpController.aidl b/core/java/android/bluetooth/IBluetoothAvrcpController.aidl index f917a50..fb61c98 100644 --- a/core/java/android/bluetooth/IBluetoothAvrcpController.aidl +++ b/core/java/android/bluetooth/IBluetoothAvrcpController.aidl @@ -1,4 +1,5 @@ /* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,6 +18,7 @@ package android.bluetooth; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothAvrcpInfo; /** * APIs for Bluetooth AVRCP controller service @@ -28,4 +30,10 @@ interface IBluetoothAvrcpController { List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states); int getConnectionState(in BluetoothDevice device); void sendPassThroughCmd(in BluetoothDevice device, int keyCode, int keyState); + void getMetaData(in int[] attributeIds); + void getPlayStatus(in int[] playStatusIds); + void getPlayerApplicationSetting(); + void setPlayerApplicationSetting(in int attributeId, in int attribVal); + BluetoothAvrcpInfo getSupportedPlayerAppSetting(in BluetoothDevice device); + int getSupportedFeatures(in BluetoothDevice device); } diff --git a/core/java/android/bluetooth/IBluetoothDun.aidl b/core/java/android/bluetooth/IBluetoothDun.aidl new file mode 100644 index 0000000..a4f2017 --- /dev/null +++ b/core/java/android/bluetooth/IBluetoothDun.aidl @@ -0,0 +1,45 @@ +/* +*Copyright (c) 2013, The Linux Foundation. All rights reserved. +* +*Redistribution and use in source and binary forms, with or without +*modification, are permitted provided that the following conditions are +*met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided +* with the distribution. +* * Neither the name of The Linux Foundation nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +*THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +*WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +*MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +*ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +*BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +*CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +*SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +*BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +*WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +*OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +*IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package android.bluetooth; + +import android.bluetooth.BluetoothDevice; + +/** + * API for Bluetooth Dun service + * + * {@hide} + */ +interface IBluetoothDun { + // Public API + boolean disconnect(in BluetoothDevice device); + int getConnectionState(in BluetoothDevice device); + List<BluetoothDevice> getConnectedDevices(); + List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states); +} diff --git a/core/java/android/bluetooth/IBluetoothHeadset.aidl b/core/java/android/bluetooth/IBluetoothHeadset.aidl index 0e23fad..0bb4088 100755 --- a/core/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/core/java/android/bluetooth/IBluetoothHeadset.aidl @@ -50,6 +50,8 @@ interface IBluetoothHeadset { boolean isAudioOn(); boolean connectAudio(); boolean disconnectAudio(); + void setAudioRouteAllowed(boolean allowed); + boolean getAudioRouteAllowed(); boolean startScoUsingVirtualVoiceCall(in BluetoothDevice device); boolean stopScoUsingVirtualVoiceCall(in BluetoothDevice device); void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type); diff --git a/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl b/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl index e518b7d..79ae4e4 100644 --- a/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl +++ b/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl @@ -62,6 +62,8 @@ interface IBluetoothHeadsetClient { int getAudioState(in BluetoothDevice device); boolean connectAudio(); boolean disconnectAudio(); + void setAudioRouteAllowed(boolean allowed); + boolean getAudioRouteAllowed(); Bundle getCurrentAgFeatures(in BluetoothDevice device); } diff --git a/core/java/android/bluetooth/IBluetoothHidDevice.aidl b/core/java/android/bluetooth/IBluetoothHidDevice.aidl new file mode 100644 index 0000000..60358c5 --- /dev/null +++ b/core/java/android/bluetooth/IBluetoothHidDevice.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2013 The Linux Foundation. All rights reserved + * Not a Contribution. + * Copyright (C) 2012 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.bluetooth; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHidDeviceAppConfiguration; +import android.bluetooth.IBluetoothHidDeviceCallback; +import android.bluetooth.BluetoothHidDeviceAppSdpSettings; +import android.bluetooth.BluetoothHidDeviceAppQosSettings; + +/** @hide */ +interface IBluetoothHidDevice { + boolean registerApp(in BluetoothHidDeviceAppConfiguration config, + in BluetoothHidDeviceAppSdpSettings sdp, in BluetoothHidDeviceAppQosSettings inQos, + in BluetoothHidDeviceAppQosSettings outQos, in IBluetoothHidDeviceCallback callback); + boolean unregisterApp(in BluetoothHidDeviceAppConfiguration config); + boolean sendReport(in int id, in byte[] data); + boolean replyReport(in byte type, in byte id, in byte[] data); + boolean reportError(byte error); + boolean unplug(); + boolean connect(); + boolean disconnect(); +} diff --git a/core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl b/core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl new file mode 100644 index 0000000..7c71a17 --- /dev/null +++ b/core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2013 The Linux Foundation. All rights reserved + * Not a Contribution. + * Copyright (C) 2011, 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.bluetooth; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHidDeviceAppConfiguration; + +/** @hide */ +interface IBluetoothHidDeviceCallback { + void onAppStatusChanged(in BluetoothDevice device, in BluetoothHidDeviceAppConfiguration config, boolean registered); + void onConnectionStateChanged(in BluetoothDevice device, in int state); + void onGetReport(in byte type, in byte id, in int bufferSize); + void onSetReport(in byte type, in byte id, in byte[] data); + void onSetProtocol(in byte protocol); + void onIntrData(in byte reportId, in byte[] data); + void onVirtualCableUnplug(); +} diff --git a/core/java/android/bluetooth/IBluetoothInputDevice.aidl b/core/java/android/bluetooth/IBluetoothInputDevice.aidl index 1ebb9ca..5bd3f78 100644 --- a/core/java/android/bluetooth/IBluetoothInputDevice.aidl +++ b/core/java/android/bluetooth/IBluetoothInputDevice.aidl @@ -56,4 +56,12 @@ interface IBluetoothInputDevice { * @hide */ boolean sendData(in BluetoothDevice device, String report); + /** + * @hide + */ + boolean getIdleTime(in BluetoothDevice device); + /** + * @hide + */ + boolean setIdleTime(in BluetoothDevice device, byte idleTime); } diff --git a/core/java/android/bluetooth/IBluetoothManager.aidl b/core/java/android/bluetooth/IBluetoothManager.aidl index 0b81ee8..bd8c6c9 100644 --- a/core/java/android/bluetooth/IBluetoothManager.aidl +++ b/core/java/android/bluetooth/IBluetoothManager.aidl @@ -19,7 +19,6 @@ package android.bluetooth; import android.bluetooth.IBluetooth; import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManagerCallback; -import android.bluetooth.IBluetoothProfileServiceConnection; import android.bluetooth.IBluetoothStateChangeCallback; /** @@ -34,14 +33,11 @@ interface IBluetoothManager void registerStateChangeCallback(in IBluetoothStateChangeCallback callback); void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback); boolean isEnabled(); - boolean enable(); + boolean enable(String callingPackage); boolean enableNoAutoConnect(); boolean disable(boolean persist); IBluetoothGatt getBluetoothGatt(); - boolean bindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy); - void unbindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy); - String getAddress(); String getName(); diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java index 2260d7e..bf0c48d 100644 --- a/core/java/android/content/BroadcastReceiver.java +++ b/core/java/android/content/BroadcastReceiver.java @@ -16,6 +16,7 @@ package android.content; +import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.ActivityThread; import android.app.IActivityManager; @@ -747,6 +748,17 @@ public abstract class BroadcastReceiver { return mPendingResult.mSendingUser; } + /** @hide */ + public String getSendingPackage(Intent intent) { + final IActivityManager mgr = ActivityManagerNative.getDefault(); + try { + boolean fg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0; + return mgr.getCallingPackageForBroadcast(fg); + } catch (RemoteException ex) { + return null; + } + } + /** * Control inclusion of debugging help for mismatched * calls to {@link Context#registerReceiver(BroadcastReceiver, IntentFilter) diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 6ede29b..863ca65 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -431,6 +431,7 @@ public abstract class ContentResolver { public final @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { + android.util.SeempLog.record_uri(13, uri); return query(uri, projection, selection, selectionArgs, sortOrder, null); } @@ -471,6 +472,7 @@ public abstract class ContentResolver { public final @Nullable Cursor query(final @NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) { + android.util.SeempLog.record_uri(13, uri); Preconditions.checkNotNull(uri, "uri"); IContentProvider unstableProvider = acquireUnstableProvider(uri); if (unstableProvider == null) { @@ -984,6 +986,7 @@ public abstract class ContentResolver { stableProvider = acquireProvider(uri); } releaseUnstableProvider(unstableProvider); + unstableProvider = null; ParcelFileDescriptor pfd = new ParcelFileDescriptorInner( fd.getParcelFileDescriptor(), stableProvider); @@ -1128,6 +1131,7 @@ public abstract class ContentResolver { stableProvider = acquireProvider(uri); } releaseUnstableProvider(unstableProvider); + unstableProvider = null; ParcelFileDescriptor pfd = new ParcelFileDescriptorInner( fd.getParcelFileDescriptor(), stableProvider); @@ -1221,6 +1225,7 @@ public abstract class ContentResolver { * @return the URL of the newly created row. */ public final @Nullable Uri insert(@NonNull Uri url, @Nullable ContentValues values) { + android.util.SeempLog.record_uri(37, url); Preconditions.checkNotNull(url, "url"); IContentProvider provider = acquireProvider(url); if (provider == null) { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 758b6ff..7ddda11 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -497,6 +497,9 @@ public abstract class Context { @ViewDebug.ExportedProperty(deepExport = true) public abstract Resources.Theme getTheme(); + /** @hide */ + public abstract void recreateTheme(); + /** * Retrieve styled attribute information in this Context's theme. See * {@link android.content.res.Resources.Theme#obtainStyledAttributes(int[])} @@ -3858,6 +3861,26 @@ public abstract class Context { int flags) throws PackageManager.NameNotFoundException; /** + * Similar to {@link #createPackageContext(String, int)}, but with a + * different {@link UserHandle}. For example, {@link #getContentResolver()} + * will open any {@link Uri} as the given user. A theme package can be + * specified which will be used when adding resources to this context + * + * @hide + */ + public abstract Context createPackageContextAsUser( + String packageName, String themePackageName, int flags, UserHandle user) + throws PackageManager.NameNotFoundException; + + /** + * Creates a context given an {@link android.content.pm.ApplicationInfo}. + * + * @hide + */ + public abstract Context createApplicationContext(ApplicationInfo application, + String themePackageName, int flags) throws PackageManager.NameNotFoundException; + + /** * Get the userId associated with this context * @return user id * diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 8359edf..795b9ae 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -123,6 +123,12 @@ public class ContextWrapper extends Context { return mBase.getTheme(); } + /** @hide */ + @Override + public void recreateTheme() { + mBase.recreateTheme(); + } + @Override public ClassLoader getClassLoader() { return mBase.getClassLoader(); @@ -757,7 +763,20 @@ public class ContextWrapper extends Context { @Override public Context createApplicationContext(ApplicationInfo application, int flags) throws PackageManager.NameNotFoundException { - return mBase.createApplicationContext(application, flags); + return createApplicationContext(application, null, flags); + } + + /** @hide */ + public Context createApplicationContext(ApplicationInfo application, + String themePackageName, int flags) throws PackageManager.NameNotFoundException { + return mBase.createApplicationContext(application, themePackageName, flags); + } + + /** @hide */ + @Override + public Context createPackageContextAsUser(String packageName, String themePackageName, + int flags, UserHandle user) throws PackageManager.NameNotFoundException { + return mBase.createPackageContextAsUser(packageName, themePackageName, flags, user); } /** @hide */ diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 87d52e4..c06f98a 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2006 The Android Open Source Project + * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -1604,6 +1605,23 @@ public class Intent implements Parcelable, Cloneable { = "android.intent.action.GET_PERMISSIONS_COUNT"; /** + * Broadcast action that requests list of all apps that have runtime permissions. It will + * respond to the request by sending a broadcast with action defined by + * {@link #EXTRA_GET_PERMISSIONS_PACKAGES_RESPONSE_INTENT}. The response will contain + * {@link #EXTRA_GET_PERMISSIONS_APP_LIST_RESULT}, as well as + * {@link #EXTRA_GET_PERMISSIONS_APP_LABEL_LIST_RESULT}, with contents described below or + * a null upon failure. + * + * <p>{@link #EXTRA_GET_PERMISSIONS_APP_LIST_RESULT} will contain a list of package names of + * apps that have runtime permissions. {@link #EXTRA_GET_PERMISSIONS_APP_LABEL_LIST_RESULT} + * will contain the list of app labels corresponding ot the apps in the first list. + * + * @hide + */ + public static final String ACTION_GET_PERMISSIONS_PACKAGES + = "android.intent.action.GET_PERMISSIONS_PACKAGES"; + + /** * Extra included in response to {@link #ACTION_GET_PERMISSIONS_COUNT}. * @hide */ @@ -1618,6 +1636,28 @@ public class Intent implements Parcelable, Cloneable { = "android.intent.extra.GET_PERMISSIONS_GROUP_LIST_RESULT"; /** + * String list of apps that have one or more runtime permissions. + * @hide + */ + public static final String EXTRA_GET_PERMISSIONS_APP_LIST_RESULT + = "android.intent.extra.GET_PERMISSIONS_APP_LIST_RESULT"; + + /** + * String list of app labels for apps that have one or more runtime permissions. + * @hide + */ + public static final String EXTRA_GET_PERMISSIONS_APP_LABEL_LIST_RESULT + = "android.intent.extra.GET_PERMISSIONS_APP_LABEL_LIST_RESULT"; + + /** + * Boolean list describing if the app is a system app for apps that have one or more runtime + * permissions. + * @hide + */ + public static final String EXTRA_GET_PERMISSIONS_IS_SYSTEM_APP_LIST_RESULT + = "android.intent.extra.GET_PERMISSIONS_IS_SYSTEM_APP_LIST_RESULT"; + + /** * Required extra to be sent with {@link #ACTION_GET_PERMISSIONS_COUNT} broadcasts. * @hide */ @@ -1625,6 +1665,13 @@ public class Intent implements Parcelable, Cloneable { = "android.intent.extra.GET_PERMISSIONS_RESONSE_INTENT"; /** + * Required extra to be sent with {@link #ACTION_GET_PERMISSIONS_PACKAGES} broadcasts. + * @hide + */ + public static final String EXTRA_GET_PERMISSIONS_PACKAGES_RESPONSE_INTENT + = "android.intent.extra.GET_PERMISSIONS_PACKAGES_RESONSE_INTENT"; + + /** * Activity action: Launch UI to manage which apps have a given permission. * <p> * Input: {@link #EXTRA_PERMISSION_NAME} specifies the permission access @@ -1800,6 +1847,15 @@ public class Intent implements Parcelable, Cloneable { */ public static final String ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS"; /** + * Broadcast Action: Update preferences for the power menu dialog. This is to provide a + * way for the preferences that need to be enabled/disabled to update because they were + * toggled elsewhere in the settings (ie profiles, immersive desktop, etc) so we don't have + * to do constant lookups while we wait for the menu to be created. Getting the values once + * when necessary is enough. + *@hide + */ + public static final String UPDATE_POWER_MENU = "android.intent.action.UPDATE_POWER_MENU"; + /** * Broadcast Action: Trigger the download and eventual installation * of a package. * <p>Input: {@link #getData} is the URI of the package file to download. @@ -2075,6 +2131,15 @@ public class Intent implements Parcelable, Cloneable { */ @Deprecated @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_WALLPAPER_CHANGED = "android.intent.action.WALLPAPER_CHANGED"; + + /** + * Broadcast Action: The current keyguard wallpaper configuration + * has changed and should be re-read. + * {@hide} + */ + public static final String ACTION_KEYGUARD_WALLPAPER_CHANGED = + "android.intent.action.KEYGUARD_WALLPAPER_CHANGED"; + /** * Broadcast Action: The current device {@link android.content.res.Configuration} * (orientation, locale, etc) has changed. When such a change happens, the @@ -2659,6 +2724,45 @@ public class Intent implements Parcelable, Cloneable { "android.intent.action.GET_RESTRICTION_ENTRIES"; /** + * <p>Broadcast Action: The state of the HOTWORD audio input has changed.:</p> + * <ul> + * <li><em>state</em> - A String value indicating the state of the input. + * {@link #EXTRA_HOTWORD_INPUT_STATE}. The value will be one of: + * {@link android.media.AudioRecord#RECORDSTATE_RECORDING} or + * {@link android.media.AudioRecord#RECORDSTATE_STOPPED}. + * </li> + * <li><em>package</em> - A String value indicating the package name of the application + * that currently holds the HOTWORD input. + * {@link #EXTRA_CURRENT_PACKAGE_NAME} + * </li> + * </ul> + * + * <p class="note">This is a protected intent that can only be sent + * by the system. It can only be received by packages that hold + * {@link android.Manifest.permission#CAPTURE_AUDIO_HOTWORD}. + * + * @hide + */ + //@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_HOTWORD_INPUT_CHANGED + = "com.cyanogenmod.intent.action.HOTWORD_INPUT_CHANGED"; + + /** + * @hide + * Activity to challenge the user for a PIN that was configured when setting up + * restrictions. Restrictions include blocking of apps and preventing certain user operations, + * controlled by {@link android.os.UserManager#setUserRestrictions(Bundle). + * Launch the activity using + * {@link android.app.Activity#startActivityForResult(Intent, int)} and check if the + * result is {@link android.app.Activity#RESULT_OK} for a successful response to the + * challenge.<p/> + * Before launching this activity, make sure that there is a PIN in effect, by calling + * {@link android.os.UserManager#hasRestrictionsChallenge()}. + */ + public static final String ACTION_RESTRICTIONS_CHALLENGE = + "android.intent.action.RESTRICTIONS_CHALLENGE"; + + /** * Sent the first time a user is starting, to allow system apps to * perform one time initialization. (This will not be seen by third * party applications because a newly initialized user does not have any @@ -2830,6 +2934,13 @@ public class Intent implements Parcelable, Cloneable { "android.intent.action.SHOW_BRIGHTNESS_DIALOG"; /** + * Activity Action: Shows the notification brightness setting dialog. + * @hide + */ + public static final String ACTION_SHOW_NOTIFICATION_BRIGHTNESS_DIALOG = + "android.intent.action.SHOW_NOTIFICATION_BRIGHTNESS_DIALOG"; + + /** * Broadcast Action: A global button was pressed. Includes a single * extra field, {@link #EXTRA_KEY_EVENT}, containing the key event that * caused the broadcast. @@ -2918,6 +3029,19 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_CREATE_DOCUMENT = "android.intent.action.CREATE_DOCUMENT"; /** + * Broadcast Action: A theme's resources were cached. Includes two extra fields, + * {@link #EXTRA_THEME_PACKAGE_NAME}, containing the package name of the theme that was + * processed, and {@link #EXTRA_THEME_RESULT}, containing the result code. + * + * <p class="note">This is a protected intent that can only be sent + * by the system.</p> + * + * @hide + */ + public static final String ACTION_THEME_RESOURCES_CACHED = + "android.intent.action.THEME_RESOURCES_CACHED"; + + /** * Activity Action: Allow the user to pick a directory subtree. When * invoked, the system will display the various {@link DocumentsProvider} * instances installed on the device, letting the user navigate through @@ -2984,6 +3108,43 @@ public class Intent implements Parcelable, Cloneable { public static final String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY"; + /** {@hide} */ + public static final String ACTION_DOZE_PULSE_STARTING = + "android.intent.action.DOZE_PULSE_STARTING"; + + /** + * Broadcast action: reports when a new thermal event has been reached. When the device + * is reaching its maximum temperatue, the thermal level reported + * {@hide} + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_THERMAL_EVENT = "android.intent.action.THERMAL_EVENT"; + + /** {@hide} */ + public static final String EXTRA_THERMAL_STATE = "android.intent.extra.THERMAL_STATE"; + + /** + * Thermal state when the device is normal. This state is sent in the + * {@link ACTION_THERMAL_EVENT} broadcast as {@link EXTRA_THERMAL_STATE}. + * {@hide} + */ + public static final int EXTRA_THERMAL_STATE_NORMAL = 0; + + /** + * Thermal state where the device is approaching its maximum threshold. This state is sent in + * the {@link ACTION_THERMAL_EVENT} broadcast as {@link EXTRA_THERMAL_STATE}. + * {@hide} + */ + public static final int EXTRA_THERMAL_STATE_WARNING = 1; + + /** + * Thermal state where the device has reached its maximum threshold. This state is sent in the + * {@link ACTION_THERMAL_EVENT} broadcast as {@link EXTRA_THERMAL_STATE}. + * {@hide} + */ + public static final int EXTRA_THERMAL_STATE_EXCEEDED = 2; + + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Standard intent categories (see addCategory()). @@ -3086,6 +3247,13 @@ public class Intent implements Parcelable, Cloneable { @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_HOME = "android.intent.category.HOME"; /** + * This is the home activity that is displayed when the device is finished setting up and ready + * for use. + * @hide + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_HOME_MAIN = "android.intent.category.HOME_MAIN"; + /** * This is the setup wizard activity, that is the first activity that is displayed * when the user sets up the device for the first time. * @hide @@ -3795,6 +3963,42 @@ public class Intent implements Parcelable, Cloneable { public static final String EXTRA_SIM_ACTIVATION_RESPONSE = "android.intent.extra.SIM_ACTIVATION_RESPONSE"; + /** + * Extra for {@link #ACTION_THEME_RESOURCES_CACHED} that provides the return value + * from processThemeResources. A value of 0 indicates a successful caching of resources. + * Error results are: + * {@link android.content.pm.PackageManager#INSTALL_FAILED_THEME_AAPT_ERROR} + * {@link android.content.pm.PackageManager#INSTALL_FAILED_THEME_IDMAP_ERROR} + * {@link android.content.pm.PackageManager#INSTALL_FAILED_THEME_UNKNOWN_ERROR} + * + * @hide + */ + public static final String EXTRA_THEME_RESULT = "android.intent.extra.RESULT"; + + /** + * Extra for {@link #ACTION_THEME_RESOURCES_CACHED} that provides the package name of the + * theme that was processed. + * + * @hide + */ + public static final String EXTRA_THEME_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME"; + + /** + * Extra for {@link #ACTION_HOTWORD_INPUT_CHANGED} that provides the state of + * the input when the broadcast action was sent. + * @hide + */ + public static final String EXTRA_HOTWORD_INPUT_STATE = + "com.cyanogenmod.intent.extra.HOTWORD_INPUT_STATE"; + + /** + * Extra for {@link #ACTION_RECENTS_LONG_PRESS} that provides the package name of the + * app in foreground when recents was long pressed. Can be reused for other purposes. + * @hide + */ + public static final String EXTRA_CURRENT_PACKAGE_NAME = + "com.cyanogenmod.intent.extra.CURRENT_PACKAGE_NAME"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Intent flags (see mFlags variable). diff --git a/core/java/android/content/ThemeVersion.java b/core/java/android/content/ThemeVersion.java new file mode 100644 index 0000000..05fbc41 --- /dev/null +++ b/core/java/android/content/ThemeVersion.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2015 The CyanogenMod 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.content; + +/** + * Warning: Careful moving/refactoring this class as our SDK references it. + * ThemeVersion 1 = CM11 + * ThemeVersion 2 = CM12/CM12.1 First Release + * ThemeVersion 3 = CM12.1 W/ Wallpaper Packs + * @hide + */ +public class ThemeVersion { + /** + * Increment this anytime changes are made to: + * 1) Changes to ThemesContract + * 2) Changes to ThemeService API + * 3) Changes to ThemeManager API + */ + public static int THEME_VERSION = 3; + + /** + * Change this if a change to the contract or service would break compatibility. + * Example: A client app like chooser might be outdated from the framework. + * It could then query the FW for this value and determine whether its safe to proceed. + */ + public static int MIN_SUPPORTED_THEME_VERSION = 2; + + /** + * Do not change the order of this. See SDK. + * Increment the minSupportedVersion when the fw can no longer support a theme's apk structure + * Increment currentVersion when a change to the theme's apk structure is changed + * For example, CM11 to CM12 introduces new resources to overlay, so the overlays + * version should change. Because the changes are not compatible with CM11, the minVersion + * must change as well. + * + * If a new feature is added to a component (ex rotations in icon packs), the current version + * for the ICON component would be incremented. If a new component is created, then add it + * to the enum list. + * + * Wallpaper Version 2: Multi wallpaper ability + * + */ + public static enum ComponentVersion { + OVERLAY(0, 2, 2), + BOOT_ANIM(1, 1, 1), + WALLPAPER(2, 1, 2), + LOCKSCREEN(3, 1, 1), + FONT(4, 1, 2), + ICON(5, 1, 1), + SOUNDS(6, 1, 1); + + public int id; + public int minSupportedVersion; + public int currentVersion; + + private ComponentVersion(int id, int minSupportedVersion, int currentVersion) { + this.id = id; + this.minSupportedVersion = minSupportedVersion; + this.currentVersion = currentVersion; + } + } +} diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index e798eb8..0105e09 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -1,6 +1,7 @@ /* * Copyright (C) 2007 The Android Open Source Project - * + * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc. + * 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 @@ -545,6 +546,16 @@ public class ActivityInfo extends ComponentInfo */ public static final int CONFIG_LAYOUT_DIRECTION = 0x2000; /** + * Bit in {@link #configChanges} that indicates a theme change occurred + * @hide + */ + public static final int CONFIG_THEME_RESOURCE = 0x100000; + /** + * Bit in {@link #configChanges} that indicates a font change occurred + * @hide + */ + public static final int CONFIG_THEME_FONT = 0x200000; + /** * Bit in {@link #configChanges} that indicates that the activity * can itself handle changes to the font scaling factor. Set from the * {@link android.R.attr#configChanges} attribute. This is diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 6feb860..1933fc9 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2007 The Android Open Source Project + * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -654,6 +655,19 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { */ public int installLocation = PackageInfo.INSTALL_LOCATION_UNSPECIFIED; + /** + * When true, indicates that any one component within this application is + * protected. + * @hide + */ + public boolean protect = false; + + /** + * Is given application theme agnostic, i.e. behaves properly when default theme is changed. + * @hide + */ + public boolean isThemeable = false; + public void dump(Printer pw, String prefix) { super.dumpFront(pw, prefix); if (className != null) { @@ -785,6 +799,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { uiOptions = orig.uiOptions; backupAgentName = orig.backupAgentName; fullBackupContent = orig.fullBackupContent; + protect = orig.protect; + isThemeable = orig.isThemeable; } @@ -838,6 +854,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeInt(descriptionRes); dest.writeInt(uiOptions); dest.writeInt(fullBackupContent); + dest.writeInt(protect ? 1 : 0); + dest.writeInt(isThemeable ? 1 : 0); } public static final Parcelable.Creator<ApplicationInfo> CREATOR @@ -890,6 +908,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { descriptionRes = source.readInt(); uiOptions = source.readInt(); fullBackupContent = source.readInt(); + protect = source.readInt() != 0; + isThemeable = source.readInt() != 0; } /** diff --git a/core/java/android/content/pm/BaseThemeInfo.java b/core/java/android/content/pm/BaseThemeInfo.java new file mode 100644 index 0000000..8ece42d --- /dev/null +++ b/core/java/android/content/pm/BaseThemeInfo.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2010, T-Mobile USA, Inc. + * + * 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.content.pm; + +import android.os.Parcelable; +import android.os.Parcel; +import android.util.Log; +import android.util.AttributeSet; +import android.content.res.Resources; + +/** + * @hide + */ +public class BaseThemeInfo implements Parcelable { + /** + * The theme id, which does not change when the theme is modified. + * Specifies an Android UI Style using style name. + * + * @see themeId attribute + * + */ + public String themeId; + + /** + * The name of the theme (as displayed by UI). + * + * @see name attribute + * + */ + public String name; + + /** + * The author name of the theme package. + * + * @see author attribute + * + */ + public String author; + + /* + * Describe the kinds of special objects contained in this Parcelable's + * marshalled representation. + * + * @return a bitmask indicating the set of special object types marshalled + * by the Parcelable. + * + * @see android.os.Parcelable#describeContents() + */ + public int describeContents() { + return 0; + } + + /* + * Flatten this object in to a Parcel. + * + * @param dest The Parcel in which the object should be written. + * @param flags Additional flags about how the object should be written. + * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}. + * + * @see android.os.Parcelable#writeToParcel(android.os.Parcel, int) + */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(themeId); + dest.writeString(name); + dest.writeString(author); + } + + /** @hide */ + public static final Parcelable.Creator<BaseThemeInfo> CREATOR + = new Parcelable.Creator<BaseThemeInfo>() { + public BaseThemeInfo createFromParcel(Parcel source) { + return new BaseThemeInfo(source); + } + + public BaseThemeInfo[] newArray(int size) { + return new BaseThemeInfo[size]; + } + }; + + /** @hide */ + public final String getResolvedString(Resources res, AttributeSet attrs, int index) { + int resId = attrs.getAttributeResourceValue(index, 0); + if (resId !=0 ) { + return res.getString(resId); + } + return attrs.getAttributeValue(index); + } + + protected BaseThemeInfo() { + } + + protected BaseThemeInfo(Parcel source) { + themeId = source.readString(); + name = source.readString(); + author = source.readString(); + } +} diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index fec2c44..51f13af 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -17,6 +17,7 @@ package android.content.pm; +import android.app.ComposedIconInfo; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; @@ -509,4 +510,20 @@ interface IPackageManager { boolean isPermissionRevokedByPolicy(String permission, String packageName, int userId); String getPermissionControllerPackageName(); + + /** Protected Apps */ + void setComponentProtectedSetting(in ComponentName componentName, + in boolean newState, int userId); + + /** Themes */ + void updateIconMapping(String pkgName); + ComposedIconInfo getComposedIconInfo(); + int processThemeResources(String themePkgName); + + /** Protected Apps */ + boolean isComponentProtected(in String callingPackage, in int callingUid, + in ComponentName componentName, int userId); + + /** protected broadcast ext */ + boolean isProtectedBroadcastAllowed(in String actionName, in int callingUid); } diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index 9e6c6b5..0de867e 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2007 The Android Open Source Project + * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +17,11 @@ package android.content.pm; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + import android.os.Parcel; import android.os.Parcelable; @@ -254,6 +260,34 @@ public class PackageInfo implements Parcelable { /** @hide */ public boolean coreApp; + // Is Theme Apk + /** + * {@hide} + */ + public boolean isThemeApk = false; + + /** + * {@hide} + */ + public boolean hasIconPack = false; + + /** + * {@hide} + */ + public ArrayList<String> mOverlayTargets; + + // Is Legacy Icon Apk + /** + * {@hide} + */ + public boolean isLegacyIconPackApk = false; + + // ThemeInfo + /** + * {@hide} + */ + public ThemeInfo themeInfo; + /** @hide */ public boolean requiredForAllUsers; @@ -323,6 +357,13 @@ public class PackageInfo implements Parcelable { dest.writeString(restrictedAccountType); dest.writeString(requiredAccountType); dest.writeString(overlayTarget); + + /* Theme-specific. */ + dest.writeInt((isThemeApk) ? 1 : 0); + dest.writeStringList(mOverlayTargets); + dest.writeParcelable(themeInfo, parcelableFlags); + dest.writeInt(hasIconPack ? 1 : 0); + dest.writeInt((isLegacyIconPackApk) ? 1 : 0); } public static final Parcelable.Creator<PackageInfo> CREATOR @@ -372,5 +413,12 @@ public class PackageInfo implements Parcelable { restrictedAccountType = source.readString(); requiredAccountType = source.readString(); overlayTarget = source.readString(); + + /* Theme-specific. */ + isThemeApk = (source.readInt() != 0); + mOverlayTargets = source.createStringArrayList(); + themeInfo = source.readParcelable(null); + hasIconPack = source.readInt() == 1; + isLegacyIconPackApk = source.readInt() == 1; } } diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java index 1efe082..d4f33fb 100644 --- a/core/java/android/content/pm/PackageInfoLite.java +++ b/core/java/android/content/pm/PackageInfoLite.java @@ -62,6 +62,7 @@ public class PackageInfoLite implements Parcelable { */ public int recommendedInstallLocation; public int installLocation; + public boolean isTheme; public VerifierInfo[] verifiers; @@ -87,6 +88,7 @@ public class PackageInfoLite implements Parcelable { dest.writeInt(recommendedInstallLocation); dest.writeInt(installLocation); dest.writeInt(multiArch ? 1 : 0); + dest.writeInt(isTheme ? 1 : 0); if (verifiers == null || verifiers.length == 0) { dest.writeInt(0); @@ -116,6 +118,7 @@ public class PackageInfoLite implements Parcelable { recommendedInstallLocation = source.readInt(); installLocation = source.readInt(); multiArch = (source.readInt() != 0); + isTheme = source.readInt() == 1 ? true : false; final int verifiersLength = source.readInt(); if (verifiersLength == 0) { diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java index 22a899c..366deb4 100644 --- a/core/java/android/content/pm/PackageItemInfo.java +++ b/core/java/android/content/pm/PackageItemInfo.java @@ -66,7 +66,14 @@ public class PackageItemInfo { * component's icon. From the "icon" attribute or, if not set, 0. */ public int icon; - + + /** + * A drawable resource identifier in the icon pack's resources + * If there isn't an icon pack or not set, then 0. + * @hide + */ + public int themedIcon; + /** * A drawable resource identifier (in the package's resources) of this * component's banner. From the "banner" attribute or, if not set, 0. @@ -110,6 +117,7 @@ public class PackageItemInfo { logo = orig.logo; metaData = orig.metaData; showUserIcon = orig.showUserIcon; + themedIcon = orig.themedIcon; } /** @@ -309,8 +317,9 @@ public class PackageItemInfo { dest.writeBundle(metaData); dest.writeInt(banner); dest.writeInt(showUserIcon); + dest.writeInt(themedIcon); } - + protected PackageItemInfo(Parcel source) { name = source.readString(); packageName = source.readString(); @@ -322,6 +331,7 @@ public class PackageItemInfo { metaData = source.readBundle(); banner = source.readInt(); showUserIcon = source.readInt(); + themedIcon = source.readInt(); } /** diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index c8e9402..8f0500e 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -830,6 +830,51 @@ public abstract class PackageManager { public static final int INSTALL_FAILED_ABORTED = -115; /** + * Used by themes + * Installation failed return code: this is passed to the {@link IPackageInstallObserver} by + * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} + * if the system failed to install the theme because aapt could not compile the app + * @hide + */ + public static final int INSTALL_FAILED_THEME_AAPT_ERROR = -400; + + /** + * Used by themes + * Installation failed return code: this is passed to the {@link IPackageInstallObserver} by + * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} + * if the system failed to install the theme because idmap failed + * apps. + * @hide + */ + public static final int INSTALL_FAILED_THEME_IDMAP_ERROR = -401; + + /** + * Used by themes + * Installation failed return code: this is passed to the {@link IPackageInstallObserver} by + * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} + * if the system failed to install the theme for an unknown reason + * apps. + * @hide + */ + public static final int INSTALL_FAILED_THEME_UNKNOWN_ERROR = -402; + + /** + * Used for prebundles + * Installation failed for a prebundled app because the user previously uninstalled it + * and we don't want to bring it back + * @hide + */ + public static final int INSTALL_FAILED_UNINSTALLED_PREBUNDLE = -403; + + /** + * Used for prebundles + * Installation failed for a prebundled app because it wasn't needed in the default + * mobile country exported by the hardware + * @hide + */ + public static final int INSTALL_FAILED_REGION_LOCKED_PREBUNDLE = -404; //bloat not found + + /** * Flag parameter for {@link #deletePackage} to indicate that you don't want to delete the * package's data directory. * @@ -1912,6 +1957,20 @@ public abstract class PackageManager { = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS"; /** + * Flag for {@link #setComponentProtectedSetting(android.content.ComponentName, boolean)}: + * This component or application has set to protected status + * @hide + */ + public static final boolean COMPONENT_PROTECTED_STATUS = false; + + /** + * Flag for {@link #setComponentProtectedSetting(android.content.ComponentName, boolean)}: + * This component or application has been explicitly set to visible status + * @hide + */ + public static final boolean COMPONENT_VISIBLE_STATUS = true; + + /** * String extra for {@link PackageInstallObserver} in the 'extras' Bundle in case of * {@link #INSTALL_FAILED_DUPLICATE_PERMISSION}. This extra names the package which provides * the existing definition for the permission. @@ -3511,6 +3570,18 @@ public abstract class PackageManager { public abstract Resources getResourcesForApplicationAsUser(String appPackageName, int userId) throws NameNotFoundException; + /** @hide */ + public abstract Resources getThemedResourcesForApplication(ApplicationInfo app, + String themePkgName) throws NameNotFoundException; + + /** @hide */ + public abstract Resources getThemedResourcesForApplication(String appPackageName, + String themePkgName) throws NameNotFoundException; + + /** @hide */ + public abstract Resources getThemedResourcesForApplicationAsUser(String appPackageName, + String themePkgName, int userId) throws NameNotFoundException; + /** * Retrieve overall information about an application package defined * in a package archive file @@ -4484,6 +4555,19 @@ public abstract class PackageManager { public abstract @NonNull PackageInstaller getPackageInstaller(); /** + * Update Component protection state + * @hide + */ + public abstract void setComponentProtectedSetting(ComponentName componentName, boolean newState); + + /** + * Return whether or not a specific component is protected + * @hide + */ + public abstract boolean isComponentProtected(String callingPackage, int callingUid, + ComponentName componentName); + + /** * Adds a {@link CrossProfileIntentFilter}. After calling this method all intents sent from the * user with id sourceUserId can also be be resolved by activities in the user with id * targetUserId if they match the specified intent filter. @@ -4709,4 +4793,22 @@ public abstract class PackageManager { } } } + + /** + * Updates the theme icon res id for the new theme + * @hide + */ + public abstract void updateIconMaps(String pkgName); + + /** + * Used to compile theme resources for a given theme + * @param themePkgName + * @return A value of 0 indicates success. Possible errors returned are: + * {@link android.content.pm.PackageManager#INSTALL_FAILED_THEME_AAPT_ERROR}, + * {@link android.content.pm.PackageManager#INSTALL_FAILED_THEME_IDMAP_ERROR}, or + * {@link android.content.pm.PackageManager#INSTALL_FAILED_THEME_UNKNOWN_ERROR} + * + * @hide + */ + public abstract int processThemeResources(String themePkgName); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 99bd390..bb46ef0 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2007 The Android Open Source Project + * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,6 +62,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; @@ -77,12 +79,17 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import java.util.jar.StrictJarFile; import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; /** * Parser for package files (APKs) on disk. This supports apps packaged either @@ -112,6 +119,17 @@ public class PackageParser { /** File name in an APK for the Android manifest. */ private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml"; + /** Path to overlay directory in a theme APK */ + private static final String OVERLAY_PATH = "assets/overlays/"; + /** Path to icon directory in a theme APK */ + private static final String ICON_PATH = "assets/icons/"; + + private static final String PACKAGE_REDIRECTIONS_XML = "res/xml/redirections.xml"; + + private static final String TAG_PACKAGE_REDIRECTIONS = "package-redirections"; + private static final String TAG_RESOURCE_REDIRECTIONS = "resource-redirections"; + private static final String TAG_ITEM = "item"; + private static final String ATTRIBUTE_ITEM_NAME = "name"; /** Path prefix for apps on expanded storage */ private static final String MNT_EXPAND = "/mnt/expand/"; @@ -251,6 +269,7 @@ public class PackageParser { public final int versionCode; public final int installLocation; public final VerifierInfo[] verifiers; + public boolean isTheme; /** Names of any split APKs, ordered by parsed splitName */ public final String[] splitNames; @@ -276,6 +295,7 @@ public class PackageParser { public final boolean multiArch; public final boolean extractNativeLibs; + public PackageLite(String codePath, ApkLite baseApk, String[] splitNames, String[] splitCodePaths, int[] splitRevisionCodes) { this.packageName = baseApk.packageName; @@ -291,6 +311,7 @@ public class PackageParser { this.coreApp = baseApk.coreApp; this.multiArch = baseApk.multiArch; this.extractNativeLibs = baseApk.extractNativeLibs; + this.isTheme = baseApk.isTheme; } public List<String> getAllCodePaths() { @@ -318,11 +339,12 @@ public class PackageParser { public final boolean coreApp; public final boolean multiArch; public final boolean extractNativeLibs; + public final boolean isTheme; public ApkLite(String codePath, String packageName, String splitName, int versionCode, int revisionCode, int installLocation, List<VerifierInfo> verifiers, Signature[] signatures, boolean coreApp, boolean multiArch, - boolean extractNativeLibs) { + boolean extractNativeLibs, boolean isTheme) { this.codePath = codePath; this.packageName = packageName; this.splitName = splitName; @@ -334,6 +356,7 @@ public class PackageParser { this.coreApp = coreApp; this.multiArch = multiArch; this.extractNativeLibs = extractNativeLibs; + this.isTheme = isTheme; } } @@ -424,6 +447,14 @@ public class PackageParser { pi.versionName = p.mVersionName; pi.sharedUserId = p.mSharedUserId; pi.sharedUserLabel = p.mSharedUserLabel; + pi.isThemeApk = p.mIsThemeApk; + pi.hasIconPack = p.hasIconPack; + pi.isLegacyIconPackApk = p.mIsLegacyIconPackApk; + + if (pi.isThemeApk) { + pi.mOverlayTargets = p.mOverlayTargets; + pi.themeInfo = p.mThemeInfo; + } pi.applicationInfo = generateApplicationInfo(p, flags, state, userId); pi.installLocation = p.installLocation; pi.coreApp = p.coreApp; @@ -614,6 +645,7 @@ public class PackageParser { public final static int PARSE_IS_PRIVILEGED = 1<<7; public final static int PARSE_COLLECT_CERTIFICATES = 1<<8; public final static int PARSE_TRUSTED_OVERLAY = 1<<9; + public final static int PARSE_IS_PREBUNDLED_DIR = 1<<10; private static final Comparator<String> sSplitNameComparator = new SplitNameComparator(); @@ -898,6 +930,18 @@ public class PackageParser { pkg.baseCodePath = apkPath; pkg.mSignatures = null; + // If the pkg is a theme, we need to know what themes it overlays + // and determine if it has an icon pack + if (pkg.mIsThemeApk) { + //Determine existance of Overlays + ArrayList<String> overlayTargets = scanPackageOverlays(apkFile); + for(String overlay : overlayTargets) { + pkg.mOverlayTargets.add(overlay); + } + + pkg.hasIconPack = packageHasIconPack(apkFile); + } + return pkg; } catch (PackageParserException e) { @@ -1020,6 +1064,68 @@ public class PackageParser { return pkg; } + + private ArrayList<String> scanPackageOverlays(File originalFile) { + Set<String> overlayTargets = new HashSet<String>(); + ZipFile privateZip = null; + try { + privateZip = new ZipFile(originalFile.getPath()); + final Enumeration<? extends ZipEntry> privateZipEntries = privateZip.entries(); + while (privateZipEntries.hasMoreElements()) { + final ZipEntry zipEntry = privateZipEntries.nextElement(); + final String zipEntryName = zipEntry.getName(); + + if (zipEntryName.startsWith(OVERLAY_PATH) && zipEntryName.length() > 16) { + String[] subdirs = zipEntryName.split("/"); + overlayTargets.add(subdirs[2]); + } + } + } catch(Exception e) { + e.printStackTrace(); + overlayTargets.clear(); + } finally { + if (privateZip != null) { + try { + privateZip.close(); + } catch (Exception e) { + //Ignore + } + } + } + + ArrayList<String> overlays = new ArrayList<String>(); + overlays.addAll(overlayTargets); + return overlays; + } + + private boolean packageHasIconPack(File originalFile) { + ZipFile privateZip = null; + try { + privateZip = new ZipFile(originalFile.getPath()); + final Enumeration<? extends ZipEntry> privateZipEntries = privateZip.entries(); + while (privateZipEntries.hasMoreElements()) { + final ZipEntry zipEntry = privateZipEntries.nextElement(); + final String zipEntryName = zipEntry.getName(); + + if (zipEntryName.startsWith(ICON_PATH) && + zipEntryName.length() > ICON_PATH.length()) { + return true; + } + } + } catch(Exception e) { + Log.e(TAG, "Could not read zip entries while checking if apk has icon pack", e); + } finally { + if (privateZip != null) { + try { + privateZip.close(); + } catch (Exception e) { + //Ignore + } + } + } + return false; + } + /** * Gathers the {@link ManifestDigest} for {@code pkg} if it exists in the * APK. If it successfully scanned the package and found the @@ -1027,6 +1133,7 @@ public class PackageParser { */ public void collectManifestDigest(Package pkg) throws PackageParserException { pkg.manifestDigest = null; + pkg.manifestHashCode = 0; // TODO: extend to gather digest for split APKs try { @@ -1035,6 +1142,7 @@ public class PackageParser { final ZipEntry je = jarFile.findEntry(ANDROID_MANIFEST_FILENAME); if (je != null) { pkg.manifestDigest = ManifestDigest.fromInputStream(jarFile.getInputStream(je)); + pkg.manifestHashCode = ThemeUtils.getPackageHashCode(pkg, jarFile); } } finally { jarFile.close(); @@ -1300,6 +1408,9 @@ public class PackageParser { // Only search the tree when the tag is directly below <manifest> int type; final int searchDepth = parser.getDepth() + 1; + // Search for category and actions inside <intent-filter> + final int iconPackSearchDepth = parser.getDepth() + 4; + boolean isTheme = false; final List<VerifierInfo> verifiers = new ArrayList<VerifierInfo>(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT @@ -1326,11 +1437,53 @@ public class PackageParser { } } } + + if (parser.getDepth() == searchDepth && "meta-data".equals(parser.getName())) { + for (int i=0; i < parser.getAttributeCount(); i++) { + if ("name".equals(parser.getAttributeName(i)) && + ThemeInfo.META_TAG_NAME.equals(parser.getAttributeValue(i))) { + isTheme = true; + installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY; + break; + } + } + } + + if (parser.getDepth() == searchDepth && "theme".equals(parser.getName())) { + isTheme = true; + installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY; + } + + if (parser.getDepth() == iconPackSearchDepth && isLegacyIconPack(parser)) { + isTheme = true; + installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY; + } } return new ApkLite(codePath, packageSplit.first, packageSplit.second, versionCode, revisionCode, installLocation, verifiers, signatures, coreApp, multiArch, - extractNativeLibs); + extractNativeLibs, isTheme); + } + + private static boolean isLegacyIconPack(XmlPullParser parser) { + boolean isAction = "action".equals(parser.getName()); + boolean isCategory = "category".equals(parser.getName()); + String[] items = isAction ? ThemeUtils.sSupportedActions + : (isCategory ? ThemeUtils.sSupportedCategories : null); + + if (items != null) { + for (int i = 0; i < parser.getAttributeCount(); i++) { + if ("name".equals(parser.getAttributeName(i))) { + final String value = parser.getAttributeValue(i); + for (String item : items) { + if (item.equals(value)) { + return true; + } + } + } + } + } + return false; } /** @@ -1382,6 +1535,8 @@ public class PackageParser { } final Package pkg = new Package(pkgName); + Bundle metaDataBundle = new Bundle(); + boolean foundApp = false; TypedArray sa = res.obtainAttributes(attrs, @@ -1722,14 +1877,18 @@ public class PackageParser { String name = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_name); + String permission = sa.getNonResourceString( + com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_permission); + sa.recycle(); if (name != null && (flags&PARSE_IS_SYSTEM) != 0) { if (pkg.protectedBroadcasts == null) { - pkg.protectedBroadcasts = new ArrayList<String>(); + pkg.protectedBroadcasts = new ArrayMap<>(); } - if (!pkg.protectedBroadcasts.contains(name)) { - pkg.protectedBroadcasts.add(name.intern()); + if (!pkg.protectedBroadcasts.containsKey(name)) { + pkg.protectedBroadcasts.put(name.intern(), + permission != null ? permission.intern() : null); } } @@ -1794,6 +1953,11 @@ public class PackageParser { XmlUtils.skipCurrentTag(parser); continue; + } else if (parser.getName().equals("meta-data")) { + if ((metaDataBundle=parseMetaData(res, parser, attrs, metaDataBundle, + outError)) == null) { + return null; + } } else if (RIGID_PARSER) { outError[0] = "Bad element under <manifest>: " + parser.getName(); @@ -1882,6 +2046,17 @@ public class PackageParser { >= android.os.Build.VERSION_CODES.DONUT)) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES; } + if (pkg.mIsThemeApk || pkg.mIsLegacyIconPackApk) { + pkg.applicationInfo.isThemeable = false; + } + + //Is this pkg a theme? + if (metaDataBundle.containsKey(ThemeInfo.META_TAG_NAME)) { + pkg.mIsThemeApk = true; + pkg.mTrustedOverlay = true; + pkg.mOverlayPriority = 1; + pkg.mThemeInfo = new ThemeInfo(metaDataBundle); + } return pkg; } @@ -2257,6 +2432,10 @@ public class PackageParser { perm.info.flags = sa.getInt( com.android.internal.R.styleable.AndroidManifestPermission_permissionFlags, 0); + perm.info.allowViaWhitelist = sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestPermission_allowViaWhitelist, + false); + sa.recycle(); if (perm.info.protectionLevel == -1) { @@ -2409,6 +2588,10 @@ public class PackageParser { final ApplicationInfo ai = owner.applicationInfo; final String pkgName = owner.applicationInfo.packageName; + String[] nonThemeablePackages = + res.getStringArray(com.android.internal.R.array.non_themeable_packages); + ai.isThemeable = isPackageThemeable(pkgName, nonThemeablePackages); + TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestApplication); @@ -3238,6 +3421,26 @@ public class PackageParser { if (!parseIntent(res, parser, attrs, true, true, intent, outError)) { return null; } + + // Check if package is a legacy icon pack + if (!owner.mIsLegacyIconPackApk) { + for(String action : ThemeUtils.sSupportedActions) { + if (intent.hasAction(action)) { + owner.mIsLegacyIconPackApk = true; + break; + } + + } + } + if (!owner.mIsLegacyIconPackApk) { + for(String category : ThemeUtils.sSupportedCategories) { + if (intent.hasCategory(category)) { + owner.mIsLegacyIconPackApk = true; + break; + } + } + } + if (intent.countActions() == 0) { Slog.w(TAG, "No actions in intent filter at " + mArchiveSourcePath + " " @@ -4247,6 +4450,22 @@ public class PackageParser { return true; } + /**1 + * Returns whether the specified package is themeable + * @param packageName Name of package to check + * @param nonThemeablePackages Array of packages that are declared as non-themeable + * @return True if the package is themeable, false otherwise + */ + private static boolean isPackageThemeable(String packageName, String[] nonThemeablePackages) { + for (String pkg : nonThemeablePackages) { + if (packageName.startsWith(pkg)) { + return false; + } + } + + return true; + } + /** * Representation of a full package parsed from APK files on disk. A package * consists of a single base APK, and zero or more split APKs. @@ -4304,7 +4523,10 @@ public class PackageParser { public final ArrayList<String> requestedPermissions = new ArrayList<String>(); - public ArrayList<String> protectedBroadcasts; + /** + * Maps from package -> permission, null for system (default behavior) + */ + public ArrayMap<String,String> protectedBroadcasts; public ArrayList<String> libraryNames = null; public ArrayList<String> usesLibraries = null; @@ -4345,6 +4567,17 @@ public class PackageParser { // For use by package manager to keep track of when a package was last used. public long mLastPackageUsageTimeInMills; + // Is Theme Apk + public boolean mIsThemeApk = false; + public final ArrayList<String> mOverlayTargets = new ArrayList<String>(0); + public Map<String, Map<String, String>> mPackageRedirections + = new HashMap<String, Map<String, String>>(); + + // Theme info + public ThemeInfo mThemeInfo = null; + + // Legacy icon pack + public boolean mIsLegacyIconPackApk = false; // // User set enabled state. // public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; @@ -4387,6 +4620,9 @@ public class PackageParser { public int mOverlayPriority; public boolean mTrustedOverlay; + public boolean hasIconPack; + public int manifestHashCode; + /** * Data used to feed the KeySetManagerService */ @@ -4739,6 +4975,12 @@ public class PackageParser { && p.usesLibraryFiles != null) { return true; } + if (state.protectedComponents != null) { + boolean protect = state.protectedComponents.size() > 0; + if (p.applicationInfo.protect != protect) { + return true; + } + } return false; } @@ -4772,6 +5014,9 @@ public class PackageParser { ai.enabled = false; } ai.enabledSetting = state.enabled; + if (state.protectedComponents != null) { + ai.protect = state.protectedComponents.size() > 0; + } } public static ApplicationInfo generateApplicationInfo(Package p, int flags, diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index 9b28401..7b6d188 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -36,6 +36,8 @@ public class PackageUserState { public ArraySet<String> disabledComponents; public ArraySet<String> enabledComponents; + public ArraySet<String> protectedComponents; + public ArraySet<String> visibleComponents; public int domainVerificationStatus; public int appLinkGeneration; @@ -62,5 +64,9 @@ public class PackageUserState { blockUninstall = o.blockUninstall; domainVerificationStatus = o.domainVerificationStatus; appLinkGeneration = o.appLinkGeneration; + protectedComponents = o.protectedComponents != null + ? new ArraySet<String>(o.protectedComponents) : null; + visibleComponents = o.visibleComponents != null + ? new ArraySet<String>(o.visibleComponents) : null; } } diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java index 9da2ba9..0fed65f 100644 --- a/core/java/android/content/pm/PermissionInfo.java +++ b/core/java/android/content/pm/PermissionInfo.java @@ -178,6 +178,14 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { */ public CharSequence nonLocalizedDescription; + /** + * Whether this permission will be granted to apps signed with white-listed keys in + * /system/etc/permissions/someapp.xml + * + * @hide + */ + public boolean allowViaWhitelist; + /** @hide */ public static int fixProtectionLevel(int level) { if (level == PROTECTION_SIGNATURE_OR_SYSTEM) { @@ -237,6 +245,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { group = orig.group; descriptionRes = orig.descriptionRes; nonLocalizedDescription = orig.nonLocalizedDescription; + allowViaWhitelist = orig.allowViaWhitelist; } /** @@ -279,6 +288,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { dest.writeInt(flags); dest.writeString(group); dest.writeInt(descriptionRes); + dest.writeInt(allowViaWhitelist ? 1 : 0); TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags); } @@ -298,6 +308,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { flags = source.readInt(); group = source.readString(); descriptionRes = source.readInt(); + allowViaWhitelist = source.readInt() == 1; nonLocalizedDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); } } diff --git a/core/java/android/content/pm/ThemeInfo.aidl b/core/java/android/content/pm/ThemeInfo.aidl new file mode 100644 index 0000000..acbc85e --- /dev/null +++ b/core/java/android/content/pm/ThemeInfo.aidl @@ -0,0 +1,3 @@ +package android.content.pm; + +parcelable ThemeInfo; diff --git a/core/java/android/content/pm/ThemeInfo.java b/core/java/android/content/pm/ThemeInfo.java new file mode 100644 index 0000000..ab798db --- /dev/null +++ b/core/java/android/content/pm/ThemeInfo.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010, T-Mobile USA, Inc. + * + * 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.content.pm; + +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParser; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.content.res.Resources; + +/** + * Overall information about "theme" package. This corresponds + * to the information collected from AndroidManifest.xml + * + * Below is an example of the manifest: + * + * <meta-data android:name="org.cyanogenmod.theme.name" android:value="Foobar's Theme"/> + * <meta-data android:name="org.cyanogenmod.theme.author" android:value="Mr.Foo" /> + * + * @hide + */ +public final class ThemeInfo extends BaseThemeInfo { + + public static final String META_TAG_NAME = "org.cyanogenmod.theme.name"; + public static final String META_TAG_AUTHOR = "org.cyanogenmod.theme.author"; + + public ThemeInfo(Bundle bundle) { + super(); + name = bundle.getString(META_TAG_NAME); + themeId = name; + author = bundle.getString(META_TAG_AUTHOR); + } + + public static final Parcelable.Creator<ThemeInfo> CREATOR + = new Parcelable.Creator<ThemeInfo>() { + public ThemeInfo createFromParcel(Parcel source) { + return new ThemeInfo(source); + } + + public ThemeInfo[] newArray(int size) { + return new ThemeInfo[size]; + } + }; + + private ThemeInfo(Parcel source) { + super(source); + } +} diff --git a/core/java/android/content/pm/ThemeUtils.java b/core/java/android/content/pm/ThemeUtils.java new file mode 100644 index 0000000..07e73b5 --- /dev/null +++ b/core/java/android/content/pm/ThemeUtils.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2014 The CyanogenMod 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.content.pm; + +import android.content.res.ThemeConfig; +import android.text.TextUtils; + +import java.io.File; +import java.io.IOException; +import java.util.jar.StrictJarFile; +import java.util.zip.ZipEntry; + +/** + * @hide + */ +public class ThemeUtils { + private static final String TAG = ThemeUtils.class.getSimpleName(); + + /* Path inside a theme APK to the overlay folder */ + public static final String OVERLAY_PATH = "assets/overlays/"; + public static final String ICONS_PATH = "assets/icons/"; + public static final String COMMON_RES_PATH = "assets/overlays/common/"; + public static final String RESOURCE_CACHE_DIR = "/data/resource-cache/"; + public static final String COMMON_RES_TARGET = "common"; + + // Package name for any app which does not have a specific theme applied + private static final String DEFAULT_PKG = "default"; + + private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF"; + + /** + * IDMAP hash version code used to alter the resulting hash and force recreating + * of the idmap. This value should be changed whenever there is a need to force + * an update to all idmaps. + */ + private static final byte IDMAP_HASH_VERSION = 3; + + // Actions in manifests which identify legacy icon packs + public static final String[] sSupportedActions = new String[] { + "org.adw.launcher.THEMES", + "com.gau.go.launcherex.theme", + "com.novalauncher.THEME" + }; + + // Categories in manifests which identify legacy icon packs + public static final String[] sSupportedCategories = new String[] { + "com.fede.launcher.THEME_ICONPACK", + "com.anddoes.launcher.THEME", + "com.teslacoilsw.launcher.THEME" + }; + + + /** + * Get the root path of the resource cache for the given theme + * @param themePkgName + * @return Root resource cache path for the given theme + */ + public static String getOverlayResourceCacheDir(String themePkgName) { + return RESOURCE_CACHE_DIR + themePkgName; + } + + /** + * Get the path of the resource cache for the given target and theme + * @param targetPkgName Target app package name + * @param themePkgName Theme package name + * @return Path to the resource cache for this target and theme + */ + public static String getTargetCacheDir(String targetPkgName, String themePkgName) { + return getOverlayResourceCacheDir(themePkgName) + File.separator + targetPkgName; + } + + /** + * Get the path to the icons for the given theme + * @param pkgName + * @return + */ + public static String getIconPackDir(String pkgName) { + return getOverlayResourceCacheDir(pkgName) + File.separator + "icons"; + } + + public static String getIconPackApkPath(String pkgName) { + return getIconPackDir(pkgName) + "/resources.apk"; + } + + public static String getIdmapPath(String targetPkgName, String overlayPkgName) { + return getTargetCacheDir(targetPkgName, overlayPkgName) + File.separator + "idmap"; + } + + public static String getOverlayPathToTarget(String targetPkgName) { + StringBuilder sb = new StringBuilder(); + sb.append(OVERLAY_PATH); + sb.append(targetPkgName); + sb.append('/'); + return sb.toString(); + } + + public static String getCommonPackageName(String themePackageName) { + if (TextUtils.isEmpty(themePackageName)) return null; + + return COMMON_RES_TARGET; + } + + /** + * Convenience method to determine if a theme component is a per app theme and not a standard + * component. + * @param component + * @return + */ + public static boolean isPerAppThemeComponent(String component) { + return !(DEFAULT_PKG.equals(component) + || ThemeConfig.SYSTEMUI_STATUS_BAR_PKG.equals(component) + || ThemeConfig.SYSTEMUI_NAVBAR_PKG.equals(component)); + } + + /** + * Get a 32 bit hashcode for the given package. + * @param pkg + * @return + */ + public static int getPackageHashCode(PackageParser.Package pkg, StrictJarFile jarFile) { + int hash = pkg.manifestDigest != null ? pkg.manifestDigest.hashCode() : 0; + final ZipEntry je = jarFile.findEntry(MANIFEST_NAME); + if (je != null) { + try { + try { + ManifestDigest digest = ManifestDigest.fromInputStream( + jarFile.getInputStream(je)); + if (digest != null) { + hash += digest.hashCode(); + } + } finally { + jarFile.close(); + } + } catch (IOException | RuntimeException e) { + // Failed to generate digest from manifest.mf + } + } + hash = 31 * hash + IDMAP_HASH_VERSION; + return hash; + } +} diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 8d96f5c..abb1871 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -21,9 +21,11 @@ import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; +import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.HashMap; /** @@ -77,6 +79,16 @@ public final class AssetManager implements AutoCloseable { private boolean mOpen = true; private HashMap<Long, RuntimeException> mRefStacks; + private String mAppName; + + private boolean mThemeSupport; + private String mThemePackageName; + private String mIconPackageName; + private String mCommonResPackageName; + private ArrayList<Integer> mThemeCookies = new ArrayList<Integer>(2); + private int mIconPackCookie; + private int mCommonResCookie; + /** * Create a new AssetManager containing only the basic system assets. * Applications will not generally use this method, instead retrieving the @@ -252,6 +264,12 @@ public final class AssetManager implements AutoCloseable { } } + /*package*/ final void recreateStringBlocks() { + synchronized (this) { + makeStringBlocks(sSystem.mStringBlocks); + } + } + /*package*/ final void makeStringBlocks(StringBlock[] seed) { final int seedNum = (seed != null) ? seed.length : 0; final int num = getStringBlockCount(); @@ -612,7 +630,9 @@ public final class AssetManager implements AutoCloseable { public final int addAssetPath(String path) { synchronized (this) { int res = addAssetPathNative(path); - makeStringBlocks(mStringBlocks); + if (mStringBlocks != null) { + makeStringBlocks(mStringBlocks); + } return res; } } @@ -627,11 +647,14 @@ public final class AssetManager implements AutoCloseable { * * {@hide} */ - - public final int addOverlayPath(String idmapPath) { + public final int addOverlayPath(String idmapPath, String themeApkPath, + String resApkPath, String targetPkgPath, String prefixPath) { synchronized (this) { - int res = addOverlayPathNative(idmapPath); - makeStringBlocks(mStringBlocks); + int res = addOverlayPathNative(idmapPath, themeApkPath, resApkPath, targetPkgPath, + prefixPath); + if (mStringBlocks != null) { + makeStringBlocks(mStringBlocks); + } return res; } } @@ -641,7 +664,59 @@ public final class AssetManager implements AutoCloseable { * * {@hide} */ - public native final int addOverlayPathNative(String idmapPath); + private native final int addOverlayPathNative(String idmapPath, String themeApkPath, + String resApkPath, String targetPkgPath, String prefixPath); + + /** + * Add a set of common assets. + * + * {@hide} + */ + public final int addCommonOverlayPath(String themeApkPath, + String resApkPath, String prefixPath) { + synchronized (this) { + return addCommonOverlayPathNative(themeApkPath, resApkPath, prefixPath); + } + } + + private native final int addCommonOverlayPathNative(String themeApkPath, + String resApkPath, String prefixPath); + + /** + * Add a set of assets as an icon pack. A pkgIdOverride value will change the package's id from + * what is in the resource table to a new value. Manage this carefully, if icon pack has more + * than one package then that next package's id will use pkgIdOverride+1. + * + * Icon packs are different from overlays as they have a different pkg id and + * do not use idmap so no targetPkg is required + * + * {@hide} + */ + public final int addIconPath(String idmapPath, String resApkPath, + String prefixPath, int pkgIdOverride) { + synchronized (this) { + return addIconPathNative(idmapPath, resApkPath, prefixPath, pkgIdOverride); + } + } + + private native final int addIconPathNative(String idmapPath, + String resApkPath, String prefixPath, int pkgIdOverride); + + /** + * Delete a set of overlay assets from the asset manager. Not for use by + * applications. Returns true if succeeded or false on failure. + * + * Also works for icon packs + * + * {@hide} + */ + public final boolean removeOverlayPath(String packageName, int cookie) { + synchronized (this) { + return removeOverlayPathNative(packageName, cookie); + } + } + + private native final boolean removeOverlayPathNative(String packageName, int cookie); /** * Add multiple sets of assets to the asset manager at once. See @@ -664,6 +739,126 @@ public final class AssetManager implements AutoCloseable { } /** + * Sets a flag indicating that this AssetManager should have themes + * attached, according to the initial request to create it by the + * ApplicationContext. + * + * {@hide} + */ + public final void setThemeSupport(boolean themeSupport) { + mThemeSupport = themeSupport; + } + + /** + * Should this AssetManager have themes attached, according to the initial + * request to create it by the ApplicationContext? + * + * {@hide} + */ + public final boolean hasThemeSupport() { + return mThemeSupport; + } + + /** + * Get package name of current icon pack (may return null). + * {@hide} + */ + public String getIconPackageName() { + return mIconPackageName; + } + + /** + * Sets icon package name + * {@hide} + */ + public void setIconPackageName(String packageName) { + mIconPackageName = packageName; + } + + /** + * Get package name of current common resources (may return null). + * {@hide} + */ + public String getCommonResPackageName() { + return mCommonResPackageName; + } + + /** + * Sets common resources package name + * {@hide} + */ + public void setCommonResPackageName(String packageName) { + mCommonResPackageName = packageName; + } + + /** + * Get package name of current theme (may return null). + * {@hide} + */ + public String getThemePackageName() { + return mThemePackageName; + } + + /** + * Sets package name and highest level style id for current theme (null, 0 is allowed). + * {@hide} + */ + public void setThemePackageName(String packageName) { + mThemePackageName = packageName; + } + + /** + * Get asset cookie for current theme (may return 0). + * {@hide} + */ + public ArrayList<Integer> getThemeCookies() { + return mThemeCookies; + } + + /** {@hide} */ + public void setIconPackCookie(int cookie) { + mIconPackCookie = cookie; + } + + /** {@hide} */ + public int getIconPackCookie() { + return mIconPackCookie; + } + + /** {@hide} */ + public void setCommonResCookie(int cookie) { + mCommonResCookie = cookie; + } + + /** {@hide} */ + public int getCommonResCookie() { + return mCommonResCookie; + } + + /** + * Sets asset cookie for current theme (0 if not a themed asset manager). + * {@hide} + */ + public void addThemeCookie(int cookie) { + mThemeCookies.add(cookie); + } + + /** {@hide} */ + public String getAppName() { + return mAppName; + } + + /** {@hide} */ + public void setAppName(String pkgName) { + mAppName = pkgName; + } + + /** {@hide} */ + public boolean hasThemedAssets() { + return mThemeCookies.size() > 0; + } + + /** * Determine whether the state in this asset manager is up-to-date with * the files on the filesystem. If false is returned, you need to * instantiate a new AssetManager class to see the new data. @@ -800,6 +995,26 @@ public final class AssetManager implements AutoCloseable { /*package*/ native final int[] getStyleAttributes(int themeRes); private native final void init(boolean isSystem); + /** + * {@hide} + */ + public native final int getBasePackageCount(); + + /** + * {@hide} + */ + public native final String getBasePackageName(int index); + + /** + * {@hide} + */ + public native final String getBaseResourcePackageName(int index); + + /** + * {@hide} + */ + public native final int getBasePackageId(int index); + private native final void destroy(); private final void incRefsLocked(long id) { diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java index da35ee9..2b8951e 100644 --- a/core/java/android/content/res/CompatibilityInfo.java +++ b/core/java/android/content/res/CompatibilityInfo.java @@ -241,8 +241,7 @@ public class CompatibilityInfo implements Parcelable { mCompatibilityFlags = compatFlags; } - private CompatibilityInfo(int compFlags, - int dens, float scale, float invertedScale) { + private CompatibilityInfo(int compFlags, int dens, float scale, float invertedScale) { mCompatibilityFlags = compFlags; applicationDensity = dens; applicationScale = scale; diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index fd60476..0bc1ec2 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2008 The Android Open Source Project + * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -82,6 +83,11 @@ public final class Configuration implements Parcelable, Comparable<Configuration public Locale locale; /** + * @hide + */ + public ThemeConfig themeConfig; + + /** * Locale should persist on setting. This is hidden because it is really * questionable whether this is the right way to expose the functionality. * @hide @@ -441,7 +447,47 @@ public final class Configuration implements Parcelable, Comparable<Configuration public static final int ORIENTATION_LANDSCAPE = 2; /** @deprecated Not currently supported or used. */ @Deprecated public static final int ORIENTATION_SQUARE = 3; - + + /** + * @hide + * @deprecated + */ + public static final String THEME_PACKAGE_NAME_PERSISTENCE_PROPERTY + = "persist.sys.themePackageName"; + + /** + * @hide + * @deprecated + */ + public static final String THEME_ICONPACK_PACKAGE_NAME_PERSISTENCE_PROPERTY + = "themeIconPackPkgName"; + + /** + * @hide + * @deprecated + */ + public static final String THEME_FONT_PACKAGE_NAME_PERSISTENCE_PROPERTY + = "themeFontPackPkgName"; + + /** + * @hide + * Serialized json structure mapping app pkgnames to their set theme. + * + * { + * "default":{ + *" stylePkgName":"com.jasonevil.theme.miuiv5dark", + * "iconPkgName":"com.cyngn.hexo", + * "fontPkgName":"com.cyngn.hexo" + * } + * } + + * If an app does not have a specific theme set then it will use the 'default' theme+ + * example: 'default' -> overlayPkgName: 'org.blue.theme' + * 'com.android.phone' -> 'com.red.theme' + * 'com.google.vending' -> 'com.white.theme' + */ + public static final String THEME_PKG_CONFIGURATION_PERSISTENCE_PROPERTY = "themeConfig"; + /** * Overall orientation of the screen. May be one of * {@link #ORIENTATION_LANDSCAPE}, {@link #ORIENTATION_PORTRAIT}. @@ -673,8 +719,11 @@ public final class Configuration implements Parcelable, Comparable<Configuration compatScreenHeightDp = o.compatScreenHeightDp; compatSmallestScreenWidthDp = o.compatSmallestScreenWidthDp; seq = o.seq; + if (o.themeConfig != null) { + themeConfig = (ThemeConfig) o.themeConfig.clone(); + } } - + public String toString() { StringBuilder sb = new StringBuilder(128); sb.append("{"); @@ -809,6 +858,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration sb.append(" s."); sb.append(seq); } + sb.append(" themeResource="); + sb.append(themeConfig); sb.append('}'); return sb.toString(); } @@ -835,6 +886,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration smallestScreenWidthDp = compatSmallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED; densityDpi = DENSITY_DPI_UNDEFINED; seq = 0; + themeConfig = null; } /** {@hide} */ @@ -977,7 +1029,18 @@ public final class Configuration implements Parcelable, Comparable<Configuration if (delta.seq != 0) { seq = delta.seq; } - + + if (delta.themeConfig != null + && (themeConfig == null || !themeConfig.equals(delta.themeConfig))) { + changed |= ActivityInfo.CONFIG_THEME_RESOURCE; + final String fontPkgName = delta.themeConfig.getFontPkgName(); + if (themeConfig == null || + (fontPkgName != null && !fontPkgName.equals(themeConfig.getFontPkgName()))) { + changed |= ActivityInfo.CONFIG_THEME_FONT; + } + themeConfig = (ThemeConfig)delta.themeConfig.clone(); + } + return changed; } @@ -1087,7 +1150,15 @@ public final class Configuration implements Parcelable, Comparable<Configuration && densityDpi != delta.densityDpi) { changed |= ActivityInfo.CONFIG_DENSITY; } - + if (delta.themeConfig != null && + (themeConfig == null || !themeConfig.equals(delta.themeConfig))) { + changed |= ActivityInfo.CONFIG_THEME_RESOURCE; + final String fontPkgName = delta.themeConfig.getFontPkgName(); + if (themeConfig == null || + (fontPkgName != null && !fontPkgName.equals(themeConfig.getFontPkgName()))) { + changed |= ActivityInfo.CONFIG_THEME_FONT; + } + } return changed; } @@ -1103,7 +1174,9 @@ public final class Configuration implements Parcelable, Comparable<Configuration * @return Return true if the resource needs to be loaded, else false. */ public static boolean needNewResources(int configChanges, int interestingChanges) { - return (configChanges & (interestingChanges|ActivityInfo.CONFIG_FONT_SCALE)) != 0; + return (configChanges & (interestingChanges | + ActivityInfo.CONFIG_FONT_SCALE | + ActivityInfo.CONFIG_THEME_RESOURCE)) != 0; } /** @@ -1176,6 +1249,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration dest.writeInt(compatScreenHeightDp); dest.writeInt(compatSmallestScreenWidthDp); dest.writeInt(seq); + dest.writeParcelable(themeConfig, flags); } public void readFromParcel(Parcel source) { @@ -1204,6 +1278,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration compatScreenHeightDp = source.readInt(); compatSmallestScreenWidthDp = source.readInt(); seq = source.readInt(); + themeConfig = source.readParcelable(ThemeConfig.class.getClassLoader()); } public static final Parcelable.Creator<Configuration> CREATOR @@ -1271,7 +1346,12 @@ public final class Configuration implements Parcelable, Comparable<Configuration n = this.smallestScreenWidthDp - that.smallestScreenWidthDp; if (n != 0) return n; n = this.densityDpi - that.densityDpi; - //if (n != 0) return n; + if (n != 0) return n; + if (this.themeConfig == null) { + if (that.themeConfig != null) return 1; + } else { + n = this.themeConfig.compareTo(that.themeConfig); + } return n; } @@ -1308,6 +1388,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration result = 31 * result + screenHeightDp; result = 31 * result + smallestScreenWidthDp; result = 31 * result + densityDpi; + result = 31 * result + (this.themeConfig != null ? + this.themeConfig.hashCode() : 0); return result; } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 731903c..7fa04f9 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -21,6 +21,9 @@ import android.annotation.ColorInt; import android.annotation.StyleRes; import android.annotation.StyleableRes; import com.android.internal.util.GrowingArrayUtils; +import android.app.ComposedIconInfo; +import android.app.IconPackHelper; +import android.app.IconPackHelper.IconCustomizer; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; @@ -45,6 +48,7 @@ import android.annotation.RawRes; import android.annotation.StringRes; import android.annotation.XmlRes; import android.content.pm.ActivityInfo; +import android.content.pm.PackageItemInfo; import android.graphics.Movie; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; @@ -59,6 +63,7 @@ import android.util.Log; import android.util.LongSparseArray; import android.util.Pools.SynchronizedPool; import android.util.Slog; +import android.util.SparseArray; import android.util.TypedValue; import android.view.ViewDebug; import android.view.ViewHierarchyEncoder; @@ -108,6 +113,22 @@ public class Resources { private static final int ID_OTHER = 0x01000004; + // Package IDs for themes. Aapt will compile the res table with this id. + /** @hide */ + public static final int THEME_FRAMEWORK_PKG_ID = 0x60; + /** @hide */ + public static final int THEME_APP_PKG_ID = 0x61; + /** @hide */ + public static final int THEME_ICON_PKG_ID = 0x62; + /** @hide */ + public static final int THEME_CM_PKG_ID = 0x63; + /** + * The common resource pkg id needs to be less than the THEME_FRAMEWORK_PKG_ID + * otherwise aapt will complain and fail + * @hide + */ + public static final int THEME_COMMON_PKG_ID = THEME_FRAMEWORK_PKG_ID - 1; + private static final Object sSync = new Object(); // Information about preloaded resources. Note that they are not @@ -158,6 +179,9 @@ public class Resources { private CompatibilityInfo mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; + private SparseArray<PackageItemInfo> mIcons; + private ComposedIconInfo mComposedIconInfo; + static { sPreloadedDrawables = new LongSparseArray[2]; sPreloadedDrawables[0] = new LongSparseArray<>(); @@ -268,7 +292,7 @@ public class Resources { mCompatibilityInfo = compatInfo; } updateConfiguration(config, metrics); - assets.ensureStringBlocks(); + assets.recreateStringBlocks(); } /** @@ -793,6 +817,19 @@ public class Resources { */ @Nullable public Drawable getDrawable(@DrawableRes int id, @Nullable Theme theme) throws NotFoundException { + return getDrawable(id, theme, true); + } + + /** @hide */ + @Nullable + public Drawable getDrawable(@DrawableRes int id, @Nullable Theme theme, boolean supportComposedIcons) + throws NotFoundException { + //Check if an icon is themed + PackageItemInfo info = mIcons != null ? mIcons.get(id) : null; + if (info != null && info.themedIcon != 0) { + id = info.themedIcon; + } + TypedValue value; synchronized (mAccessLock) { value = mTmpValue; @@ -801,9 +838,24 @@ public class Resources { } else { mTmpValue = null; } - getValue(id, value, true); + getValue(id, value, true, supportComposedIcons); + } + Drawable res = null; + try { + res = loadDrawable(value, id, theme); + } catch (NotFoundException e) { + // The below statement will be true if we were trying to load a composed icon. + // Since we received a NotFoundException, try to load the original if this + // condition is true, otherwise throw the original exception. + if (supportComposedIcons && mComposedIconInfo != null && info != null && + info.themedIcon == 0) { + Log.e(TAG, "Failed to retrieve composed icon.", e); + getValue(id, value, true, false); + res = loadDrawable(value, id, theme); + } else { + throw e; + } } - final Drawable res = loadDrawable(value, id, theme); synchronized (mAccessLock) { if (mTmpValue == null) { mTmpValue = value; @@ -860,6 +912,19 @@ public class Resources { */ @Nullable public Drawable getDrawableForDensity(@DrawableRes int id, int density, @Nullable Theme theme) { + return getDrawableForDensity(id, density, theme, true); + } + + /** @hide */ + @Nullable + public Drawable getDrawableForDensity(@DrawableRes int id, int density, @Nullable Theme theme, + boolean supportComposedIcons) { + //Check if an icon was themed + PackageItemInfo info = mIcons != null ? mIcons.get(id) : null; + if (info != null && info.themedIcon != 0) { + id = info.themedIcon; + } + TypedValue value; synchronized (mAccessLock) { value = mTmpValue; @@ -868,7 +933,7 @@ public class Resources { } else { mTmpValue = null; } - getValueForDensity(id, density, value, true); + getValueForDensity(id, density, value, true, supportComposedIcons); /* * Pretend the requested density is actually the display density. If @@ -1344,8 +1409,24 @@ public class Resources { */ public void getValue(@AnyRes int id, TypedValue outValue, boolean resolveRefs) throws NotFoundException { + getValue(id, outValue, resolveRefs, true); + } + + /** @hide */ + public void getValue(@AnyRes int id, TypedValue outValue, boolean resolveRefs, + boolean supportComposedIcons) throws NotFoundException { + //Check if an icon was themed + PackageItemInfo info = mIcons != null ? mIcons.get(id) : null; + if (info != null && info.themedIcon != 0) { + id = info.themedIcon; + } boolean found = mAssets.getResourceValue(id, 0, outValue, resolveRefs); if (found) { + if (supportComposedIcons && IconPackHelper.shouldComposeIcon(mComposedIconInfo) + && info != null && info.themedIcon == 0) { + Drawable dr = loadDrawable(outValue, id, null); + IconCustomizer.getValue(this, id, outValue, dr); + } return; } throw new NotFoundException("Resource ID #0x" @@ -1367,8 +1448,45 @@ public class Resources { */ public void getValueForDensity(@AnyRes int id, int density, TypedValue outValue, boolean resolveRefs) throws NotFoundException { + getValueForDensity(id, density, outValue, resolveRefs, true); + } + + /** @hide */ + public void getValueForDensity(@AnyRes int id, int density, TypedValue outValue, + boolean resolveRefs, boolean supportComposedIcons) throws NotFoundException { + //Check if an icon was themed + PackageItemInfo info = mIcons != null ? mIcons.get(id) : null; + if (info != null && info.themedIcon != 0) { + id = info.themedIcon; + } + boolean found = mAssets.getResourceValue(id, density, outValue, resolveRefs); if (found) { + if (supportComposedIcons && IconPackHelper.shouldComposeIcon(mComposedIconInfo) && + info != null && info.themedIcon == 0) { + int tmpDensity = outValue.density; + /* + * Pretend the requested density is actually the display density. If + * the drawable returned is not the requested density, then force it + * to be scaled later by dividing its density by the ratio of + * requested density to actual device density. Drawables that have + * undefined density or no density don't need to be handled here. + */ + if (outValue.density > 0 && outValue.density != TypedValue.DENSITY_NONE) { + if (outValue.density == density) { + outValue.density = mMetrics.densityDpi; + } else { + outValue.density = (outValue.density * mMetrics.densityDpi) / density; + } + } + Drawable dr = loadDrawable(outValue, id, null); + + // Return to original density. If we do not do this then + // the caller will get the wrong density for the given id and perform + // more of its own scaling in loadDrawable + outValue.density = tmpDensity; + IconCustomizer.getValue(this, id, outValue, dr); + } return; } throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)); @@ -1438,10 +1556,12 @@ public class Resources { * if not already defined in the theme. */ public void applyStyle(int resId, boolean force) { - AssetManager.applyThemeStyle(mTheme, resId, force); + synchronized (mKey) { + AssetManager.applyThemeStyle(mTheme, resId, force); - mThemeResId = resId; - mKey.append(resId, force); + mThemeResId = resId; + mKey.append(resId, force); + } } /** @@ -1454,10 +1574,14 @@ public class Resources { * @param other The existing Theme to copy from. */ public void setTo(Theme other) { - AssetManager.copyTheme(mTheme, other.mTheme); + synchronized (mKey) { + synchronized (other.mKey) { + AssetManager.copyTheme(mTheme, other.mTheme); - mThemeResId = other.mThemeResId; - mKey.setTo(other.getKey()); + mThemeResId = other.mThemeResId; + mKey.setTo(other.getKey()); + } + } } /** @@ -1480,11 +1604,13 @@ public class Resources { * @see #obtainStyledAttributes(AttributeSet, int[], int, int) */ public TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) { - final int len = attrs.length; - final TypedArray array = TypedArray.obtain(Resources.this, len); - array.mTheme = this; - AssetManager.applyStyle(mTheme, 0, 0, 0, attrs, array.mData, array.mIndices); - return array; + synchronized (mKey) { + final int len = attrs.length; + final TypedArray array = TypedArray.obtain(Resources.this, len); + array.mTheme = this; + AssetManager.applyStyle(mTheme, 0, 0, 0, attrs, array.mData, array.mIndices); + return array; + } } /** @@ -1494,7 +1620,7 @@ public class Resources { * <p>Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done * with the array. * - * @param resid The desired style resource. + * @param resId The desired style resource. * @param attrs The desired attributes in the style. * * @throws NotFoundException Throws NotFoundException if the given ID does not exist. @@ -1507,39 +1633,15 @@ public class Resources { * @see #obtainStyledAttributes(int[]) * @see #obtainStyledAttributes(AttributeSet, int[], int, int) */ - public TypedArray obtainStyledAttributes(@StyleRes int resid, @StyleableRes int[] attrs) + public TypedArray obtainStyledAttributes(@StyleRes int resId, @StyleableRes int[] attrs) throws NotFoundException { - final int len = attrs.length; - final TypedArray array = TypedArray.obtain(Resources.this, len); - array.mTheme = this; - if (false) { - int[] data = array.mData; - - System.out.println("**********************************************************"); - System.out.println("**********************************************************"); - System.out.println("**********************************************************"); - System.out.println("Attributes:"); - String s = " Attrs:"; - int i; - for (i=0; i<attrs.length; i++) { - s = s + " 0x" + Integer.toHexString(attrs[i]); - } - System.out.println(s); - s = " Found:"; - TypedValue value = new TypedValue(); - for (i=0; i<attrs.length; i++) { - int d = i*AssetManager.STYLE_NUM_ENTRIES; - value.type = data[d+AssetManager.STYLE_TYPE]; - value.data = data[d+AssetManager.STYLE_DATA]; - value.assetCookie = data[d+AssetManager.STYLE_ASSET_COOKIE]; - value.resourceId = data[d+AssetManager.STYLE_RESOURCE_ID]; - s = s + " 0x" + Integer.toHexString(attrs[i]) - + "=" + value; - } - System.out.println(s); + synchronized (mKey) { + final int len = attrs.length; + final TypedArray array = TypedArray.obtain(Resources.this, len); + array.mTheme = this; + AssetManager.applyStyle(mTheme, 0, resId, 0, attrs, array.mData, array.mIndices); + return array; } - AssetManager.applyStyle(mTheme, 0, resid, 0, attrs, array.mData, array.mIndices); - return array; } /** @@ -1592,50 +1694,23 @@ public class Resources { */ public TypedArray obtainStyledAttributes(AttributeSet set, @StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { - final int len = attrs.length; - final TypedArray array = TypedArray.obtain(Resources.this, len); - - // XXX note that for now we only work with compiled XML files. - // To support generic XML files we will need to manually parse - // out the attributes from the XML file (applying type information - // contained in the resources and such). - final XmlBlock.Parser parser = (XmlBlock.Parser)set; - AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes, - parser != null ? parser.mParseState : 0, attrs, array.mData, array.mIndices); + synchronized (mKey) { + final int len = attrs.length; + final TypedArray array = TypedArray.obtain(Resources.this, len); - array.mTheme = this; - array.mXml = parser; + // XXX note that for now we only work with compiled XML files. + // To support generic XML files we will need to manually parse + // out the attributes from the XML file (applying type information + // contained in the resources and such). + final XmlBlock.Parser parser = (XmlBlock.Parser) set; + AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes, + parser != null ? parser.mParseState : 0, + attrs, array.mData, array.mIndices); + array.mTheme = this; + array.mXml = parser; - if (false) { - int[] data = array.mData; - - System.out.println("Attributes:"); - String s = " Attrs:"; - int i; - for (i=0; i<set.getAttributeCount(); i++) { - s = s + " " + set.getAttributeName(i); - int id = set.getAttributeNameResource(i); - if (id != 0) { - s = s + "(0x" + Integer.toHexString(id) + ")"; - } - s = s + "=" + set.getAttributeValue(i); - } - System.out.println(s); - s = " Found:"; - TypedValue value = new TypedValue(); - for (i=0; i<attrs.length; i++) { - int d = i*AssetManager.STYLE_NUM_ENTRIES; - value.type = data[d+AssetManager.STYLE_TYPE]; - value.data = data[d+AssetManager.STYLE_DATA]; - value.assetCookie = data[d+AssetManager.STYLE_ASSET_COOKIE]; - value.resourceId = data[d+AssetManager.STYLE_RESOURCE_ID]; - s = s + " 0x" + Integer.toHexString(attrs[i]) - + "=" + value; - } - System.out.println(s); + return array; } - - return array; } /** @@ -1654,18 +1729,20 @@ public class Resources { */ @NonNull public TypedArray resolveAttributes(@NonNull int[] values, @NonNull int[] attrs) { - final int len = attrs.length; - if (values == null || len != values.length) { - throw new IllegalArgumentException( - "Base attribute values must the same length as attrs"); - } + synchronized (mKey) { + final int len = attrs.length; + if (values == null || len != values.length) { + throw new IllegalArgumentException( + "Base attribute values must the same length as attrs"); + } - final TypedArray array = TypedArray.obtain(Resources.this, len); - AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); - array.mTheme = this; - array.mXml = null; + final TypedArray array = TypedArray.obtain(Resources.this, len); + AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); + array.mTheme = this; + array.mXml = null; - return array; + return array; + } } /** @@ -1686,14 +1763,9 @@ public class Resources { * <var>outValue</var> is valid, else false. */ public boolean resolveAttribute(int resid, TypedValue outValue, boolean resolveRefs) { - boolean got = mAssets.getThemeValue(mTheme, resid, outValue, resolveRefs); - if (false) { - System.out.println( - "resolveAttribute #" + Integer.toHexString(resid) - + " got=" + got + ", type=0x" + Integer.toHexString(outValue.type) - + ", data=0x" + Integer.toHexString(outValue.data)); + synchronized (mKey) { + return mAssets.getThemeValue(mTheme, resid, outValue, resolveRefs); } - return got; } /** @@ -1739,8 +1811,11 @@ public class Resources { * @see ActivityInfo */ public int getChangingConfigurations() { - final int nativeChangingConfig = AssetManager.getThemeChangingConfigurations(mTheme); - return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig); + synchronized (mKey) { + final int nativeChangingConfig = + AssetManager.getThemeChangingConfigurations(mTheme); + return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig); + } } /** @@ -1751,7 +1826,9 @@ public class Resources { * @param prefix Text to prefix each line printed. */ public void dump(int priority, String tag, String prefix) { - AssetManager.dumpTheme(mTheme, priority, tag, prefix); + synchronized (mKey) { + AssetManager.dumpTheme(mTheme, priority, tag, prefix); + } } @Override @@ -1801,19 +1878,21 @@ public class Resources { */ @ViewDebug.ExportedProperty(category = "theme", hasAdjacentMapping = true) public String[] getTheme() { - final int N = mKey.mCount; - final String[] themes = new String[N * 2]; - for (int i = 0, j = N - 1; i < themes.length; i += 2, --j) { - final int resId = mKey.mResId[j]; - final boolean forced = mKey.mForce[j]; - try { - themes[i] = getResourceName(resId); - } catch (NotFoundException e) { - themes[i] = Integer.toHexString(i); + synchronized (mKey) { + final int N = mKey.mCount; + final String[] themes = new String[N * 2]; + for (int i = 0, j = N - 1; i < themes.length; i += 2, --j) { + final int resId = mKey.mResId[j]; + final boolean forced = mKey.mForce[j]; + try { + themes[i] = getResourceName(resId); + } catch (NotFoundException e) { + themes[i] = Integer.toHexString(i); + } + themes[i + 1] = forced ? "forced" : "not forced"; } - themes[i + 1] = forced ? "forced" : "not forced"; + return themes; } - return themes; } /** @hide */ @@ -1834,13 +1913,15 @@ public class Resources { * @hide */ public void rebase() { - AssetManager.clearTheme(mTheme); - - // Reapply the same styles in the same order. - for (int i = 0; i < mKey.mCount; i++) { - final int resId = mKey.mResId[i]; - final boolean force = mKey.mForce[i]; - AssetManager.applyThemeStyle(mTheme, resId, force); + synchronized (mKey) { + AssetManager.clearTheme(mTheme); + + // Reapply the same styles in the same order. + for (int i = 0; i < mKey.mCount; i++) { + final int resId = mKey.mResId[i]; + final boolean force = mKey.mForce[i]; + AssetManager.applyThemeStyle(mTheme, resId, force); + } } } } @@ -2005,8 +2086,13 @@ public class Resources { mConfiguration.setLayoutDirection(mConfiguration.locale); } if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) { - mMetrics.densityDpi = mConfiguration.densityDpi; - mMetrics.density = mConfiguration.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE; + if (DisplayMetrics.DENSITY_DEVICE_DEFAULT == mCompatibilityInfo.applicationDensity + && (config != null + && config.densityDpi == DisplayMetrics.DENSITY_DEVICE_DEFAULT)) { + mMetrics.setDensity(DisplayMetrics.DENSITY_PREFERRED); + } else { + mMetrics.setDensity(mConfiguration.densityDpi); + } } mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale; @@ -2083,7 +2169,15 @@ public class Resources { mTmpConfig.setLayoutDirection(mTmpConfig.locale); } configChanges = mConfiguration.updateFrom(mTmpConfig); - configChanges = ActivityInfo.activityInfoConfigToNative(configChanges); + + /* This is ugly, but modifying the activityInfoConfigToNative + * adapter would be messier */ + if ((configChanges & ActivityInfo.CONFIG_THEME_RESOURCE) != 0) { + configChanges = ActivityInfo.activityInfoConfigToNative(configChanges); + configChanges |= ActivityInfo.CONFIG_THEME_RESOURCE; + } else { + configChanges = ActivityInfo.activityInfoConfigToNative(configChanges); + } } return configChanges; } @@ -2428,7 +2522,7 @@ public class Resources { } sPreloaded = true; mPreloading = true; - sPreloadedDensity = DisplayMetrics.DENSITY_DEVICE; + sPreloadedDensity = DisplayMetrics.DENSITY_PREFERRED; mConfiguration.densityDpi = sPreloadedDensity; updateConfiguration(null, null); } @@ -2526,9 +2620,10 @@ public class Resources { // attributes. final ConstantState cs; if (isColorDrawable) { - cs = sPreloadedColorDrawables.get(key); + cs = mAssets.hasThemedAssets() ? null : sPreloadedColorDrawables.get(key); } else { - cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key); + cs = mAssets.hasThemedAssets() ? null : + sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key); } Drawable dr; @@ -2666,7 +2761,7 @@ public class Resources { if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && value.type <= TypedValue.TYPE_LAST_COLOR_INT) { final android.content.res.ConstantState<ColorStateList> factory = - sPreloadedColorStateLists.get(key); + mAssets.hasThemedAssets() ? null : sPreloadedColorStateLists.get(key); if (factory != null) { return factory.newInstance(); } @@ -2690,7 +2785,7 @@ public class Resources { } final android.content.res.ConstantState<ColorStateList> factory = - sPreloadedColorStateLists.get(key); + mAssets.hasThemedAssets() ? null : sPreloadedColorStateLists.get(key); if (factory != null) { csl = factory.newInstance(this, theme); } @@ -2845,6 +2940,28 @@ public class Resources { return theme.obtainStyledAttributes(set, attrs, 0, 0); } + /** @hide */ + public void setIconResources(SparseArray<PackageItemInfo> icons) { + mIcons = icons; + } + + /** @hide */ + public void setComposedIconInfo(ComposedIconInfo iconInfo) { + mComposedIconInfo = iconInfo; + } + + /** @hide */ + public ComposedIconInfo getComposedIconInfo() { + return mComposedIconInfo; + } + + /** @hide */ + public final void updateStringCache() { + synchronized (mAccessLock) { + mAssets.recreateStringBlocks(); + } + } + private Resources() { mAssets = AssetManager.getSystem(); // NOTE: Intentionally leaving this uninitialized (all values set diff --git a/core/java/android/content/res/ResourcesKey.java b/core/java/android/content/res/ResourcesKey.java index 2620571..6cbf1d7 100644 --- a/core/java/android/content/res/ResourcesKey.java +++ b/core/java/android/content/res/ResourcesKey.java @@ -24,25 +24,31 @@ import java.util.Objects; public final class ResourcesKey { private final String mResDir; private final float mScale; + private final boolean mIsThemeable; private final int mHash; + private final ThemeConfig mThemeConfig; public final int mDisplayId; @NonNull public final Configuration mOverrideConfiguration; public ResourcesKey(String resDir, int displayId, Configuration overrideConfiguration, - float scale) { + float scale, boolean isThemeable, ThemeConfig themeConfig) { mResDir = resDir; mDisplayId = displayId; mOverrideConfiguration = overrideConfiguration != null ? overrideConfiguration : Configuration.EMPTY; mScale = scale; + mIsThemeable = isThemeable; + mThemeConfig = themeConfig; int hash = 17; hash = 31 * hash + (mResDir == null ? 0 : mResDir.hashCode()); hash = 31 * hash + mDisplayId; hash = 31 * hash + mOverrideConfiguration.hashCode(); hash = 31 * hash + Float.floatToIntBits(mScale); + hash = 31 * hash + (mIsThemeable ? 1 : 0); + hash = 31 * hash + (themeConfig != null ? themeConfig.hashCode() : 0); mHash = hash; } @@ -74,6 +80,17 @@ public final class ResourcesKey { if (mScale != peer.mScale) { return false; } + if (mIsThemeable != peer.mIsThemeable) { + return false; + } + if (mThemeConfig != peer.mThemeConfig) { + if (mThemeConfig == null || peer.mThemeConfig == null) { + return false; + } + if (!mThemeConfig.equals(peer.mThemeConfig)) { + return false; + } + } return true; } diff --git a/core/java/android/content/res/ThemeConfig.java b/core/java/android/content/res/ThemeConfig.java new file mode 100644 index 0000000..f304801 --- /dev/null +++ b/core/java/android/content/res/ThemeConfig.java @@ -0,0 +1,579 @@ +/* + * Copyright (C) 2016 The CyanogenMod Project + * Portions copyright (C) 2014, T-Mobile USA, Inc. + * + * 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.content.res; + +import android.content.ContentResolver; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.UserHandle; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.JsonReader; +import android.util.JsonToken; +import android.util.JsonWriter; +import android.util.Log; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; +import java.util.Collections; +import java.util.Map; + +/** + * The Theme Configuration allows lookup of a theme element (fonts, icon, overlay) for a given + * application. If there isn't a particular theme designated to an app, it will fallback on the + * default theme. If there isn't a default theme then it will simply fallback to holo. + * + * @hide + */ +public class ThemeConfig implements Cloneable, Parcelable, Comparable<ThemeConfig> { + public static final String TAG = ThemeConfig.class.getCanonicalName(); + public static final String SYSTEM_DEFAULT = "system"; + + /** + * Special package name for theming the navbar separate from the rest of SystemUI + */ + public static final String SYSTEMUI_NAVBAR_PKG = "com.android.systemui.navbar"; + public static final String SYSTEMUI_STATUS_BAR_PKG = "com.android.systemui"; + + // Key for any app which does not have a specific theme applied + private static final String KEY_DEFAULT_PKG = "default"; + private static final SystemConfig mSystemConfig = new SystemConfig(); + private static final SystemAppTheme mSystemAppTheme = new SystemAppTheme(); + + // Maps pkgname to theme (ex com.angry.birds -> red theme) + protected final Map<String, AppTheme> mThemes = new ArrayMap<>(); + + public ThemeConfig(Map<String, AppTheme> appThemes) { + mThemes.putAll(appThemes); + } + + public String getOverlayPkgName() { + AppTheme theme = getDefaultTheme(); + return theme.mOverlayPkgName; + } + + public String getOverlayForStatusBar() { + return getOverlayPkgNameForApp(SYSTEMUI_STATUS_BAR_PKG); + } + + public String getOverlayForNavBar() { + return getOverlayPkgNameForApp(SYSTEMUI_NAVBAR_PKG); + } + + public String getOverlayPkgNameForApp(String appPkgName) { + AppTheme theme = getThemeFor(appPkgName); + return theme.mOverlayPkgName; + } + + public String getIconPackPkgName() { + AppTheme theme = getDefaultTheme(); + return theme.mIconPkgName; + } + + public String getIconPackPkgNameForApp(String appPkgName) { + AppTheme theme = getThemeFor(appPkgName); + return theme.mIconPkgName; + } + + public String getFontPkgName() { + AppTheme defaultTheme = getDefaultTheme(); + return defaultTheme.mFontPkgName; + } + + public String getFontPkgNameForApp(String appPkgName) { + AppTheme theme = getThemeFor(appPkgName); + return theme.mFontPkgName; + } + + public Map<String, AppTheme> getAppThemes() { + return Collections.unmodifiableMap(mThemes); + } + + private AppTheme getThemeFor(String pkgName) { + AppTheme theme = mThemes.get(pkgName); + if (theme == null) theme = getDefaultTheme(); + return theme; + } + + private AppTheme getDefaultTheme() { + AppTheme theme = mThemes.get(KEY_DEFAULT_PKG); + if (theme == null) theme = mSystemAppTheme; + return theme; + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof ThemeConfig) { + ThemeConfig o = (ThemeConfig) object; + + Map<String, AppTheme> currThemes = (mThemes == null) ? + new ArrayMap<String, AppTheme>() : mThemes; + Map<String, AppTheme> newThemes = (o.mThemes == null) ? + new ArrayMap<String, AppTheme>() : o.mThemes; + + return currThemes.equals(newThemes); + } + return false; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + if (mThemes != null) { + result.append("themes:"); + result.append(mThemes); + } + return result.toString(); + } + + @Override + public int hashCode() { + int hash = 17; + hash = 31 * hash + mThemes.hashCode(); + return hash; + } + + public String toJson() { + return JsonSerializer.toJson(this); + } + + public static ThemeConfig fromJson(String json) { + return JsonSerializer.fromJson(json); + } + + /** + * Represents the theme that the device booted into. This is used to + * simulate a "default" configuration based on the user's last known + * preference until the theme is switched at runtime. + */ + public static ThemeConfig getBootTheme(ContentResolver resolver) { + return getBootThemeForUser(resolver, UserHandle.USER_OWNER); + } + + public static ThemeConfig getBootThemeForUser(ContentResolver resolver, int userHandle) { + ThemeConfig bootTheme = mSystemConfig; + try { + String json = Settings.Secure.getStringForUser(resolver, + Configuration.THEME_PKG_CONFIGURATION_PERSISTENCE_PROPERTY, userHandle); + bootTheme = ThemeConfig.fromJson(json); + + // Handle upgrade Case: Previously the theme configuration was in separate fields + if (bootTheme == null) { + String overlayPkgName = Settings.Secure.getStringForUser(resolver, + Configuration.THEME_PACKAGE_NAME_PERSISTENCE_PROPERTY, userHandle); + String iconPackPkgName = Settings.Secure.getStringForUser(resolver, + Configuration.THEME_ICONPACK_PACKAGE_NAME_PERSISTENCE_PROPERTY, userHandle); + String fontPkgName = Settings.Secure.getStringForUser(resolver, + Configuration.THEME_FONT_PACKAGE_NAME_PERSISTENCE_PROPERTY, userHandle); + + Builder builder = new Builder(); + builder.defaultOverlay(overlayPkgName); + builder.defaultIcon(iconPackPkgName); + builder.defaultFont(fontPkgName); + bootTheme = builder.build(); + } + } catch (SecurityException e) { + Log.w(TAG, "Could not get boot theme"); + } + return bootTheme; + } + + /** + * Represents the system framework theme, perceived by the system as there + * being no theme applied. + */ + public static ThemeConfig getSystemTheme() { + return mSystemConfig; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + String json = JsonSerializer.toJson(this); + dest.writeString(json); + } + + public static final Parcelable.Creator<ThemeConfig> CREATOR = + new Parcelable.Creator<ThemeConfig>() { + public ThemeConfig createFromParcel(Parcel source) { + String json = source.readString(); + ThemeConfig themeConfig = JsonSerializer.fromJson(json); + return themeConfig; + } + + public ThemeConfig[] newArray(int size) { + return new ThemeConfig[size]; + } + }; + + @Override + public int compareTo(ThemeConfig o) { + if (o == null) return -1; + int n = 0; + n = mThemes.equals(o.mThemes) ? 0 : 1; + return n; + } + + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + Log.d(TAG, "clone not supported", e); + return null; + } + } + + public static class AppTheme implements Cloneable, Comparable<AppTheme> { + // If any field is modified or added here be sure to change the serializer accordingly + String mOverlayPkgName; + String mIconPkgName; + String mFontPkgName; + + public AppTheme(String overlayPkgName, String iconPkgName, String fontPkgName) { + mOverlayPkgName = overlayPkgName; + mIconPkgName = iconPkgName; + mFontPkgName = fontPkgName; + } + + public String getIconPackPkgName() { + return mIconPkgName; + } + + public String getOverlayPkgName() { + return mOverlayPkgName; + } + + public String getFontPackPkgName() { + return mFontPkgName; + } + + @Override + public synchronized int hashCode() { + int hash = 17; + hash = 31 * hash + (mOverlayPkgName == null ? 0 : mOverlayPkgName.hashCode()); + hash = 31 * hash + (mIconPkgName == null ? 0 : mIconPkgName.hashCode()); + hash = 31 * hash + (mFontPkgName == null ? 0 : mFontPkgName.hashCode()); + return hash; + } + + @Override + public int compareTo(AppTheme o) { + if (o == null) return -1; + int n = 0; + n = mIconPkgName.compareTo(o.mIconPkgName); + if (n != 0) return n; + n = mFontPkgName.compareTo(o.mFontPkgName); + if (n != 0) return n; + n = mOverlayPkgName.equals(o.mOverlayPkgName) ? 0 : 1; + return n; + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (object instanceof AppTheme) { + AppTheme o = (AppTheme) object; + String currentOverlayPkgName = (mOverlayPkgName == null)? "" : mOverlayPkgName; + String newOverlayPkgName = (o.mOverlayPkgName == null)? "" : o.mOverlayPkgName; + String currentIconPkgName = (mIconPkgName == null)? "" : mIconPkgName; + String newIconPkgName = (o.mIconPkgName == null)? "" : o.mIconPkgName; + String currentFontPkgName = (mFontPkgName == null)? "" : mFontPkgName; + String newFontPkgName = (o.mFontPkgName == null)? "" : o.mFontPkgName; + + + return (currentIconPkgName.equals(newIconPkgName) && + currentFontPkgName.equals(newFontPkgName) && + currentOverlayPkgName.equals(newOverlayPkgName)); + } + return false; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + if (mOverlayPkgName != null) { + result.append("overlay:"); + result.append(mOverlayPkgName); + } + + if (!TextUtils.isEmpty(mIconPkgName)) { + result.append(", iconPack:"); + result.append(mIconPkgName); + } + + if (!TextUtils.isEmpty(mFontPkgName)) { + result.append(", fontPkg:"); + result.append(mFontPkgName); + } + return result.toString(); + } + } + + + public static class Builder { + private Map<String, String> mOverlays = new ArrayMap<>(); + private Map<String, String> mIcons = new ArrayMap<>(); + private Map<String, String> mFonts = new ArrayMap<>(); + + public Builder() {} + + public Builder(ThemeConfig theme) { + for(Map.Entry<String, AppTheme> entry : theme.mThemes.entrySet()) { + String key = entry.getKey(); + AppTheme appTheme = entry.getValue(); + mFonts.put(key, appTheme.getFontPackPkgName()); + mIcons.put(key, appTheme.getIconPackPkgName()); + mOverlays.put(key, appTheme.getOverlayPkgName()); + } + } + + /** + * For uniquely theming a specific app. ex. "Dialer gets red theme, + * Calculator gets blue theme" + */ + public Builder defaultOverlay(String themePkgName) { + if (themePkgName != null) { + mOverlays.put(KEY_DEFAULT_PKG, themePkgName); + } else { + mOverlays.remove(KEY_DEFAULT_PKG); + } + return this; + } + + public Builder defaultFont(String themePkgName) { + if (themePkgName != null) { + mFonts.put(KEY_DEFAULT_PKG, themePkgName); + } else { + mFonts.remove(KEY_DEFAULT_PKG); + } + return this; + } + + public Builder defaultIcon(String themePkgName) { + if (themePkgName != null) { + mIcons.put(KEY_DEFAULT_PKG, themePkgName); + } else { + mIcons.remove(KEY_DEFAULT_PKG); + } + return this; + } + + public Builder icon(String appPkgName, String themePkgName) { + if (themePkgName != null) { + mIcons.put(appPkgName, themePkgName); + } else { + mIcons.remove(appPkgName); + } + return this; + } + + public Builder overlay(String appPkgName, String themePkgName) { + if (themePkgName != null) { + mOverlays.put(appPkgName, themePkgName); + } else { + mOverlays.remove(appPkgName); + } + return this; + } + + public Builder font(String appPkgName, String themePkgName) { + if (themePkgName != null) { + mFonts.put(appPkgName, themePkgName); + } else { + mFonts.remove(appPkgName); + } + return this; + } + + public ThemeConfig build() { + ArraySet<String> appPkgSet = new ArraySet<>(); + appPkgSet.addAll(mOverlays.keySet()); + appPkgSet.addAll(mIcons.keySet()); + appPkgSet.addAll(mFonts.keySet()); + + Map<String, AppTheme> appThemes = new ArrayMap<>(); + for(String appPkgName : appPkgSet) { + String icon = mIcons.get(appPkgName); + String overlay = mOverlays.get(appPkgName); + String font = mFonts.get(appPkgName); + + // Remove app theme if all items are null + if (overlay == null && icon == null && font == null) { + if (appThemes.containsKey(appPkgName)) { + appThemes.remove(appPkgName); + } + } else { + AppTheme appTheme = new AppTheme(overlay, icon, font); + appThemes.put(appPkgName, appTheme); + } + } + ThemeConfig themeConfig = new ThemeConfig(appThemes); + return themeConfig; + } + } + + + public static class JsonSerializer { + private static final String NAME_OVERLAY_PKG = "mOverlayPkgName"; + private static final String NAME_ICON_PKG = "mIconPkgName"; + private static final String NAME_FONT_PKG = "mFontPkgName"; + + public static String toJson(ThemeConfig theme) { + String json = null; + Writer writer = null; + JsonWriter jsonWriter = null; + try { + writer = new StringWriter(); + jsonWriter = new JsonWriter(writer); + writeTheme(jsonWriter, theme); + json = writer.toString(); + } catch(IOException e) { + Log.e(TAG, "Could not write theme mapping", e); + } finally { + closeQuietly(writer); + closeQuietly(jsonWriter); + } + return json; + } + + private static void writeTheme(JsonWriter writer, ThemeConfig theme) + throws IOException { + writer.beginObject(); + for(Map.Entry<String, AppTheme> entry : theme.mThemes.entrySet()) { + String appPkgName = entry.getKey(); + AppTheme appTheme = entry.getValue(); + writer.name(appPkgName); + writeAppTheme(writer, appTheme); + } + writer.endObject(); + } + + private static void writeAppTheme(JsonWriter writer, AppTheme appTheme) throws IOException { + writer.beginObject(); + writer.name(NAME_OVERLAY_PKG).value(appTheme.mOverlayPkgName); + writer.name(NAME_ICON_PKG).value(appTheme.mIconPkgName); + writer.name(NAME_FONT_PKG).value(appTheme.mFontPkgName); + writer.endObject(); + } + + public static ThemeConfig fromJson(String json) { + if (json == null) return null; + Map<String, AppTheme> map = new ArrayMap<>(); + StringReader reader = null; + JsonReader jsonReader = null; + try { + reader = new StringReader(json); + jsonReader = new JsonReader(reader); + jsonReader.beginObject(); + while (jsonReader.hasNext()) { + String appPkgName = jsonReader.nextName(); + AppTheme appTheme = readAppTheme(jsonReader); + map.put(appPkgName, appTheme); + } + jsonReader.endObject(); + } catch(Exception e) { + Log.e(TAG, "Could not parse ThemeConfig from: " + json, e); + } finally { + closeQuietly(reader); + closeQuietly(jsonReader); + } + return new ThemeConfig(map); + } + + private static AppTheme readAppTheme(JsonReader reader) throws IOException { + String overlay = null; + String icon = null; + String font = null; + + reader.beginObject(); + while(reader.hasNext()) { + String name = reader.nextName(); + if (NAME_OVERLAY_PKG.equals(name) && reader.peek() != JsonToken.NULL) { + overlay = reader.nextString(); + } else if (NAME_ICON_PKG.equals(name) && reader.peek() != JsonToken.NULL) { + icon = reader.nextString(); + } else if (NAME_FONT_PKG.equals(name) && reader.peek() != JsonToken.NULL) { + font = reader.nextString(); + } else { + reader.skipValue(); + } + } + reader.endObject(); + + return new AppTheme(overlay, icon, font); + } + + private static void closeQuietly(Reader reader) { + try { + if (reader != null) reader.close(); + } catch(IOException e) { + } + } + + private static void closeQuietly(JsonReader reader) { + try { + if (reader != null) reader.close(); + } catch(IOException e) { + } + } + + private static void closeQuietly(Writer writer) { + try { + if (writer != null) writer.close(); + } catch(IOException e) { + } + } + + private static void closeQuietly(JsonWriter writer) { + try { + if (writer != null) writer.close(); + } catch(IOException e) { + } + } + } + + public static class SystemConfig extends ThemeConfig { + public SystemConfig() { + super(new ArrayMap<String, AppTheme>()); + } + } + + public static class SystemAppTheme extends AppTheme { + public SystemAppTheme() { + super(SYSTEM_DEFAULT, SYSTEM_DEFAULT, SYSTEM_DEFAULT); + } + + @Override + public String toString() { + return "No Theme Applied (System)"; + } + } +} diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java index 3cda39a..6ab9637 100644 --- a/core/java/android/database/sqlite/SQLiteConnection.java +++ b/core/java/android/database/sqlite/SQLiteConnection.java @@ -387,6 +387,8 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen } finally { execute(success ? "COMMIT" : "ROLLBACK", null, null); } + } catch (SQLiteDatabaseCorruptException ex) { + throw ex; } catch (RuntimeException ex) { throw new SQLiteException("Failed to change locale for db '" + mConfiguration.label + "' to '" + newLocale + "'.", ex); diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 1fc69c0..dd15d38 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -40,6 +40,7 @@ import android.util.Log; import android.text.TextUtils; import android.view.Surface; import android.view.SurfaceHolder; +import android.os.SystemProperties; import java.io.IOException; import java.lang.ref.WeakReference; @@ -153,6 +154,10 @@ public class Camera { private static final int CAMERA_MSG_RAW_IMAGE_NOTIFY = 0x200; private static final int CAMERA_MSG_PREVIEW_METADATA = 0x400; private static final int CAMERA_MSG_FOCUS_MOVE = 0x800; + /* ### QC ADD-ONS: START */ + private static final int CAMERA_MSG_STATS_DATA = 0x1000; + private static final int CAMERA_MSG_META_DATA = 0x2000; + /* ### QC ADD-ONS: END */ private long mNativeContext; // accessed by native methods private EventHandler mEventHandler; @@ -180,6 +185,10 @@ public class Camera { private static final int ENOSYS = -38; private static final int EUSERS = -87; private static final int EOPNOTSUPP = -95; + /* ### QC ADD-ONS: START */ + private CameraDataCallback mCameraDataCallback; + private CameraMetaDataCallback mCameraMetaDataCallback; + /* ### QC ADD-ONS: END */ /** * Broadcast Action: A new picture is taken by the camera, and the entry of @@ -266,6 +275,17 @@ public class Camera { */ public static final int CAMERA_FACING_FRONT = 1; + /* ### QC ADD-ONS: START TBD*/ + /** @hide + * camera is in ZSL mode. + */ + public static final int CAMERA_SUPPORT_MODE_ZSL = 2; + + /** @hide + * camera is in non-ZSL mode. + */ + public static final int CAMERA_SUPPORT_MODE_NONZSL = 3; + /* ### QC ADD-ONS: END */ /** * The direction that the camera faces. It should be * CAMERA_FACING_BACK or CAMERA_FACING_FRONT. @@ -450,6 +470,10 @@ public class Camera { mPostviewCallback = null; mUsingPreviewAllocation = false; mZoomListener = null; + /* ### QC ADD-ONS: START */ + mCameraDataCallback = null; + mCameraMetaDataCallback = null; + /* ### QC ADD-ONS: END */ Looper looper; if ((looper = Looper.myLooper()) != null) { @@ -460,8 +484,21 @@ public class Camera { mEventHandler = null; } - return native_setup(new WeakReference<Camera>(this), cameraId, halVersion, - ActivityThread.currentOpPackageName()); + String packageName = ActivityThread.currentOpPackageName(); + + //Force HAL1 if the package name falls in this bucket + String packageList = SystemProperties.get("camera.hal1.packagelist", ""); + if (packageList.length() > 0) { + TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(','); + splitter.setString(packageList); + for (String str : splitter) { + if (packageName.equals(str)) { + halVersion = CAMERA_HAL_API_VERSION_1_0; + break; + } + } + } + return native_setup(new WeakReference<Camera>(this), cameraId, halVersion, packageName); } private int cameraInitNormal(int cameraId) { @@ -766,6 +803,7 @@ public class Camera { * @see android.media.MediaActionSound */ public final void setPreviewCallback(PreviewCallback cb) { + android.util.SeempLog.record(66); mPreviewCallback = cb; mOneShot = false; mWithBuffer = false; @@ -792,6 +830,7 @@ public class Camera { * @see android.media.MediaActionSound */ public final void setOneShotPreviewCallback(PreviewCallback cb) { + android.util.SeempLog.record(68); mPreviewCallback = cb; mOneShot = true; mWithBuffer = false; @@ -830,6 +869,7 @@ public class Camera { * @see android.media.MediaActionSound */ public final void setPreviewCallbackWithBuffer(PreviewCallback cb) { + android.util.SeempLog.record(67); mPreviewCallback = cb; mOneShot = false; mWithBuffer = true; @@ -1152,7 +1192,23 @@ public class Camera { mAutoFocusMoveCallback.onAutoFocusMoving(msg.arg1 == 0 ? false : true, mCamera); } return; + /* ### QC ADD-ONS: START */ + case CAMERA_MSG_STATS_DATA: + int statsdata[] = new int[257]; + for(int i =0; i<257; i++ ) { + statsdata[i] = byteToInt( (byte[])msg.obj, i*4); + } + if (mCameraDataCallback != null) { + mCameraDataCallback.onCameraData(statsdata, mCamera); + } + return; + case CAMERA_MSG_META_DATA: + if (mCameraMetaDataCallback != null) { + mCameraMetaDataCallback.onCameraMetaData((byte[])msg.obj, mCamera); + } + return; + /* ### QC ADD-ONS: END */ default: Log.e(TAG, "Unknown message type " + msg.what); return; @@ -1376,6 +1432,7 @@ public class Camera { */ public final void takePicture(ShutterCallback shutter, PictureCallback raw, PictureCallback jpeg) { + android.util.SeempLog.record(65); takePicture(shutter, raw, null, jpeg); } private native final void native_takePicture(int msgType); @@ -1411,6 +1468,7 @@ public class Camera { */ public final void takePicture(ShutterCallback shutter, PictureCallback raw, PictureCallback postview, PictureCallback jpeg) { + android.util.SeempLog.record(65); mShutterCallback = shutter; mRawImageCallback = raw; mPostviewCallback = postview; @@ -1579,6 +1637,20 @@ public class Camera { private native final boolean _enableShutterSound(boolean enabled); /** + * Send a vendor-specific camera command + * + * @hide + */ + public final void sendVendorCommand(int cmd, int arg1, int arg2) { + if (cmd < 1000) { + throw new IllegalArgumentException("Command numbers must be at least 1000"); + } + _sendVendorCommand(cmd, arg1, arg2); + } + + private native final void _sendVendorCommand(int cmd, int arg1, int arg2); + + /** * Callback interface for zoom changes during a smooth zoom operation. * * @see #setZoomChangeListener(OnZoomChangeListener) @@ -1800,6 +1872,23 @@ public class Camera { * as a set. Either they are all valid, or none of them are. */ public Point mouth = null; + + /** + * {@hide} + */ + public int smileDegree = 0; + /** + * {@hide} + */ + public int smileScore = 0; + /** + * {@hide} + */ + public int blinkDetected = 0; + /** + * {@hide} + */ + public int faceRecognised = 0; } /** @@ -1892,6 +1981,27 @@ public class Camera { return p; } + /** @hide + * Returns the current cct value of white balance. + * + * If it's in AWB mode, cct is determined by stats/awb module. + * + * If it's in Manual WB mode, it actually returns cct value + * set by user via {@link #setParameters(Camera.Parameters)}. + */ + public int getWBCurrentCCT() { + Parameters p = new Parameters(); + String s = native_getParameters(); + p.unflatten(s); + + int cct = 0; + if (p.getWBCurrentCCT() != null) { + cct = Integer.parseInt(p.getWBCurrentCCT()); + } + + return cct; + } + /** * Returns an empty {@link Parameters} for testing purpose. * @@ -1904,6 +2014,166 @@ public class Camera { return camera.new Parameters(); } + /* ### QC ADD-ONS: START */ + private static int byteToInt(byte[] b, int offset) { + int value = 0; + for (int i = 0; i < 4; i++) { + int shift = (4 - 1 - i) * 8; + value += (b[(3-i) + offset] & 0x000000FF) << shift; + } + return value; + } + /** @hide + * Handles the callback for when Camera Data is available. + * data is read from the camera. + */ + public interface CameraDataCallback { + /** + * Callback for when camera data is available. + * + * @param data a int array of the camera data + * @param camera the Camera service object + */ + void onCameraData(int[] data, Camera camera); + }; + + /** @hide + * Set camera histogram mode and registers a callback function to run. + * Only valid after startPreview() has been called. + * + * @param cb the callback to run + */ + public final void setHistogramMode(CameraDataCallback cb) + { + mCameraDataCallback = cb; + native_setHistogramMode(cb!=null); + } + private native final void native_setHistogramMode(boolean mode); + + /** @hide + * Set camera histogram command to send data. + * + */ + public final void sendHistogramData() + { + native_sendHistogramData(); + } + private native final void native_sendHistogramData(); + + /** @hide + * Handles the callback for when Camera Meta Data is available. + * Meta data is read from the camera. + */ + public interface CameraMetaDataCallback { + /** + * Callback for when camera meta data is available. + * + * @param data a byte array of the camera meta data + * @param camera the Camera service object + */ + void onCameraMetaData(byte[] data, Camera camera); + }; + + /** @hide + * Set camera meta data and registers a callback function to run. + * Only valid after startPreview() has been called. + * + * @param cb the callback to run + */ + public final void setMetadataCb(CameraMetaDataCallback cb) + { + mCameraMetaDataCallback = cb; + native_setMetadataCb(cb!=null); + } + private native final void native_setMetadataCb(boolean mode); + + /** @hide + * Set camera face detection command to send meta data. + */ + public final void sendMetaData() + { + native_sendMetaData(); + } + private native final void native_sendMetaData(); + + /** @hide + * Configure longshot mode. Available only in ZSL. + * + * @param enable enable/disable this mode + */ + public final void setLongshot(boolean enable) + { + native_setLongshot(enable); + } + private native final void native_setLongshot(boolean enable); + + /** @hide + * Stop longshot. Available only in ZSL. + */ + public final void stopLongshot() + { + native_stopLongshot(); + } + private native final void native_stopLongshot(); + + /** @hide + * Handles the Touch Co-ordinate. + */ + public class Coordinate { + /** + * Sets the x,y co-ordinates for a touch event + * + * @param x the x co-ordinate (pixels) + * @param y the y co-ordinate (pixels) + */ + public Coordinate(int x, int y) { + xCoordinate = x; + yCoordinate = y; + } + /** + * Compares {@code obj} to this co-ordinate. + * + * @param obj the object to compare this co-ordinate with. + * @return {@code true} if the xCoordinate and yCoordinate of {@code obj} is the + * same as those of this coordinate. {@code false} otherwise. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Coordinate)) { + return false; + } + Coordinate c = (Coordinate) obj; + return xCoordinate == c.xCoordinate && yCoordinate == c.yCoordinate; + } + + /** x co-ordinate for the touch event*/ + public int xCoordinate; + + /** y co-ordinate for the touch event */ + public int yCoordinate; + }; + + /** @hide + * Returns the current focus position. + * + * If it's in AF mode, it's the lens position after af is done. + * + * If it's in Manual Focus mode, it actually returns the value + * set by user via {@link #setParameters(Camera.Parameters)}. + */ + public int getCurrentFocusPosition() { + Parameters p = new Parameters(); + String s = native_getParameters(); + p.unflatten(s); + + int focus_pos = -1; + if (p.getCurrentFocusPosition() != null) { + focus_pos = Integer.parseInt(p.getCurrentFocusPosition()); + } + return focus_pos; + } + + /* ### QC ADD-ONS: END */ /** * Returns a copied {@link Parameters}; for shim use only. * @@ -2151,6 +2421,10 @@ public class Camera { public static final String WHITE_BALANCE_CLOUDY_DAYLIGHT = "cloudy-daylight"; public static final String WHITE_BALANCE_TWILIGHT = "twilight"; public static final String WHITE_BALANCE_SHADE = "shade"; + /** @hide + * wb manual cct mode. + */ + public static final String WHITE_BALANCE_MANUAL_CCT = "manual-cct"; // Values for color effect settings. public static final String EFFECT_NONE = "none"; @@ -2198,6 +2472,11 @@ public class Camera { */ public static final String FLASH_MODE_TORCH = "torch"; + /** @hide + * Scene mode is off. + */ + public static final String SCENE_MODE_ASD = "asd"; + /** * Scene mode is off. */ @@ -2274,6 +2553,14 @@ public class Camera { * Capture the naturally warm color of scenes lit by candles. */ public static final String SCENE_MODE_CANDLELIGHT = "candlelight"; + /** @hide + * SCENE_MODE_BACKLIGHT + **/ + public static final String SCENE_MODE_BACKLIGHT = "backlight"; + /** @hide + * SCENE_MODE_FLOWERS + **/ + public static final String SCENE_MODE_FLOWERS = "flowers"; /** * Applications are looking for a barcode. Camera driver will be @@ -2316,6 +2603,13 @@ public class Camera { */ public static final String FOCUS_MODE_FIXED = "fixed"; + /** @hide + * Normal focus mode. Applications should call + * {@link #autoFocus(AutoFocusCallback)} to start the focus in this + * mode. + */ + public static final String FOCUS_MODE_NORMAL = "normal"; + /** * Extended depth of field (EDOF). Focusing is done digitally and * continuously. Applications should not call {@link @@ -2368,6 +2662,11 @@ public class Camera { */ public static final String FOCUS_MODE_CONTINUOUS_PICTURE = "continuous-picture"; + /** @hide + * manual focus mode + */ + public static final String FOCUS_MODE_MANUAL_POSITION = "manual"; + // Indices for focus distance array. /** * The array index of near focus distance for use with @@ -2404,11 +2703,15 @@ public class Camera { // Formats for setPreviewFormat and setPictureFormat. private static final String PIXEL_FORMAT_YUV422SP = "yuv422sp"; private static final String PIXEL_FORMAT_YUV420SP = "yuv420sp"; + private static final String PIXEL_FORMAT_YUV420SP_ADRENO = "yuv420sp-adreno"; private static final String PIXEL_FORMAT_YUV422I = "yuv422i-yuyv"; private static final String PIXEL_FORMAT_YUV420P = "yuv420p"; private static final String PIXEL_FORMAT_RGB565 = "rgb565"; private static final String PIXEL_FORMAT_JPEG = "jpeg"; private static final String PIXEL_FORMAT_BAYER_RGGB = "bayer-rggb"; + private static final String PIXEL_FORMAT_RAW = "raw"; + private static final String PIXEL_FORMAT_YV12 = "yv12"; + private static final String PIXEL_FORMAT_NV12 = "nv12"; /** * Order matters: Keys that are {@link #set(String, String) set} later @@ -3182,8 +3485,8 @@ public class Camera { } /** - * Sets GPS processing method. It will store up to 32 characters - * in JPEG EXIF header. + * Sets GPS processing method. The method will be stored in a UTF-8 string up to 31 bytes + * long, in the JPEG EXIF header. * * @param processing_method The processing method to get this location. */ @@ -3196,8 +3499,11 @@ public class Camera { * parameters. */ public void removeGpsData() { + remove(KEY_QC_GPS_LATITUDE_REF); remove(KEY_GPS_LATITUDE); + remove(KEY_QC_GPS_LONGITUDE_REF); remove(KEY_GPS_LONGITUDE); + remove(KEY_QC_GPS_ALTITUDE_REF); remove(KEY_GPS_ALTITUDE); remove(KEY_GPS_TIMESTAMP); remove(KEY_GPS_PROCESSING_METHOD); @@ -4088,6 +4394,7 @@ public class Camera { splitter.setString(str); int index = 0; for (String s : splitter) { + s = s.replaceAll("\\s",""); output[index++] = Integer.parseInt(s); } } @@ -4158,7 +4465,7 @@ public class Camera { // Example string: "(10000,26623),(10000,30000)". Return null if the // passing string is null or the size is 0. private ArrayList<int[]> splitRange(String str) { - if (str == null || str.charAt(0) != '(' + if (str == null || str.isEmpty() || str.charAt(0) != '(' || str.charAt(str.length() - 1) != ')') { Log.e(TAG, "Invalid range list string=" + str); return null; @@ -4183,7 +4490,7 @@ public class Camera { // Example string: "(-10,-10,0,0,300),(0,0,10,10,700)". Return null if // the passing string is null or the size is 0 or (0,0,0,0,0). private ArrayList<Area> splitArea(String str) { - if (str == null || str.charAt(0) != '(' + if (str == null || str.isEmpty() || str.charAt(0) != '(' || str.charAt(str.length() - 1) != ')') { Log.e(TAG, "Invalid area string=" + str); return null; @@ -4220,5 +4527,1231 @@ public class Camera { if (s1 != null && s1.equals(s2)) return true; return false; } + /* ### QC ADD-ONS: START */ + + /* ### QC ADDED PARAMETER KEYS*/ + private static final String KEY_QC_HFR_SIZE = "hfr-size"; + private static final String KEY_QC_PREVIEW_FRAME_RATE_MODE = "preview-frame-rate-mode"; + private static final String KEY_QC_PREVIEW_FRAME_RATE_AUTO_MODE = "frame-rate-auto"; + private static final String KEY_QC_PREVIEW_FRAME_RATE_FIXED_MODE = "frame-rate-fixed"; + private static final String KEY_QC_GPS_LATITUDE_REF = "gps-latitude-ref"; + private static final String KEY_QC_GPS_LONGITUDE_REF = "gps-longitude-ref"; + private static final String KEY_QC_GPS_ALTITUDE_REF = "gps-altitude-ref"; + private static final String KEY_QC_GPS_STATUS = "gps-status"; + private static final String KEY_QC_EXIF_DATETIME = "exif-datetime"; + private static final String KEY_QC_TOUCH_AF_AEC = "touch-af-aec"; + private static final String KEY_QC_TOUCH_INDEX_AEC = "touch-index-aec"; + private static final String KEY_QC_TOUCH_INDEX_AF = "touch-index-af"; + private static final String KEY_QC_MANUAL_FOCUS_POSITION = "manual-focus-position"; + private static final String KEY_QC_MANUAL_FOCUS_POS_TYPE = "manual-focus-pos-type"; + private static final String KEY_QC_SCENE_DETECT = "scene-detect"; + private static final String KEY_QC_ISO_MODE = "iso"; + private static final String KEY_QC_EXPOSURE_TIME = "exposure-time"; + private static final String KEY_QC_MIN_EXPOSURE_TIME = "min-exposure-time"; + private static final String KEY_QC_MAX_EXPOSURE_TIME = "max-exposure-time"; + private static final String KEY_QC_LENSSHADE = "lensshade"; + private static final String KEY_QC_HISTOGRAM = "histogram"; + private static final String KEY_QC_SKIN_TONE_ENHANCEMENT = "skinToneEnhancement"; + private static final String KEY_QC_AUTO_EXPOSURE = "auto-exposure"; + private static final String KEY_QC_SHARPNESS = "sharpness"; + private static final String KEY_QC_MAX_SHARPNESS = "max-sharpness"; + private static final String KEY_QC_CONTRAST = "contrast"; + private static final String KEY_QC_MAX_CONTRAST = "max-contrast"; + private static final String KEY_QC_SATURATION = "saturation"; + private static final String KEY_QC_MAX_SATURATION = "max-saturation"; + private static final String KEY_QC_DENOISE = "denoise"; + private static final String KEY_QC_CONTINUOUS_AF = "continuous-af"; + private static final String KEY_QC_SELECTABLE_ZONE_AF = "selectable-zone-af"; + private static final String KEY_QC_FACE_DETECTION = "face-detection"; + private static final String KEY_QC_MEMORY_COLOR_ENHANCEMENT = "mce"; + private static final String KEY_QC_REDEYE_REDUCTION = "redeye-reduction"; + private static final String KEY_QC_ZSL = "zsl"; + private static final String KEY_QC_CAMERA_MODE = "camera-mode"; + private static final String KEY_QC_VIDEO_HIGH_FRAME_RATE = "video-hfr"; + private static final String KEY_QC_VIDEO_HDR = "video-hdr"; + private static final String KEY_QC_POWER_MODE = "power-mode"; + private static final String KEY_QC_POWER_MODE_SUPPORTED = "power-mode-supported"; + private static final String KEY_QC_WB_MANUAL_CCT = "wb-manual-cct"; + private static final String KEY_QC_MIN_WB_CCT = "min-wb-cct"; + private static final String KEY_QC_MAX_WB_CCT = "max-wb-cct"; + private static final String KEY_QC_AUTO_HDR_ENABLE = "auto-hdr-enable"; + private static final String KEY_QC_VIDEO_ROTATION = "video-rotation"; + + /** @hide + * KEY_QC_AE_BRACKET_HDR + **/ + public static final String KEY_QC_AE_BRACKET_HDR = "ae-bracket-hdr"; + + /* ### QC ADDED PARAMETER VALUES*/ + + // Values for touch af/aec settings. + /** @hide + * TOUCH_AF_AEC_OFF + **/ + public static final String TOUCH_AF_AEC_OFF = "touch-off"; + /** @hide + * TOUCH_AF_AEC_ON + **/ + public static final String TOUCH_AF_AEC_ON = "touch-on"; + + // Values for auto exposure settings. + /** @hide + * Auto exposure frame-avg + **/ + public static final String AUTO_EXPOSURE_FRAME_AVG = "frame-average"; + /** @hide + * Auto exposure center weighted + **/ + public static final String AUTO_EXPOSURE_CENTER_WEIGHTED = "center-weighted"; + /** @hide + * Auto exposure spot metering + **/ + public static final String AUTO_EXPOSURE_SPOT_METERING = "spot-metering"; + + //Values for ISO settings + /** @hide + * ISO_AUTO + **/ + public static final String ISO_AUTO = "auto"; + /** @hide + * ISO_HJR + **/ + public static final String ISO_HJR = "ISO_HJR"; + /** @hide + * ISO_100 + **/ + public static final String ISO_100 = "ISO100"; + /** @hide + * ISO_200 + **/ + public static final String ISO_200 = "ISO200"; + /** @hide + * ISO_400 + **/ + public static final String ISO_400 = "ISO400"; + /** @hide + * ISO_800 + **/ + public static final String ISO_800 = "ISO800"; + /** @hide + * ISO_1600 + **/ + public static final String ISO_1600 = "ISO1600"; + + /** @hide + * ISO_3200 + **/ + public static final String ISO_3200 = "ISO3200"; + + //Values for Lens Shading + /** @hide + * LENSSHADE_ENABLE + **/ + public static final String LENSSHADE_ENABLE = "enable"; + /** @hide + * LENSSHADE_DISABLE + **/ + public static final String LENSSHADE_DISABLE= "disable"; + + //Values for Histogram + /** @hide + * Histogram enable + **/ + public static final String HISTOGRAM_ENABLE = "enable"; + /** @hide + * Histogram disable + **/ + public static final String HISTOGRAM_DISABLE= "disable"; + + //Values for Skin Tone Enhancement + /** @hide + * SKIN_TONE_ENHANCEMENT_ENABLE + **/ + public static final String SKIN_TONE_ENHANCEMENT_ENABLE = "enable"; + /** @hide + * SKIN_TONE_ENHANCEMENT_DISABLE + **/ + public static final String SKIN_TONE_ENHANCEMENT_DISABLE= "disable"; + + // Values for MCE settings. + /** @hide + * MCE_ENaBLE + **/ + public static final String MCE_ENABLE = "enable"; + /** @hide + * MCE_DISABLE + **/ + public static final String MCE_DISABLE = "disable"; + + // Values for ZSL settings. + /** @hide + * ZSL_ON + **/ + public static final String ZSL_ON = "on"; + /** @hide + * ZSL_OFF + **/ + public static final String ZSL_OFF = "off"; + + // Values for HDR Bracketing settings. + + /** @hide + * AEC bracketing off + **/ + public static final String AE_BRACKET_HDR_OFF = "Off"; + /** @hide + * AEC bracketing hdr + **/ + public static final String AE_BRACKET_HDR = "HDR"; + /** @hide + * AEC bracketing aec-bracket + **/ + public static final String AE_BRACKET = "AE-Bracket"; + + // Values for Power mode. + /** @hide + * LOW_POWER + **/ + public static final String LOW_POWER = "Low_Power"; + /** @hide + * NORMAL_POWER + **/ + public static final String NORMAL_POWER = "Normal_Power"; + + // Values for HFR settings. + /** @hide + * VIDEO_HFR_OFF + **/ + public static final String VIDEO_HFR_OFF = "off"; + /** @hide + * VIDEO_HFR_2X + **/ + public static final String VIDEO_HFR_2X = "60"; + /** @hide + * VIDEO_HFR_3X + **/ + public static final String VIDEO_HFR_3X = "90"; + /** @hide + * VIDEO_HFR_4X + **/ + public static final String VIDEO_HFR_4X = "120"; + + // Values for auto scene detection settings. + /** @hide + * SCENE_DETECT_OFF + **/ + public static final String SCENE_DETECT_OFF = "off"; + /** @hide + * SCENE_DETECT_ON + **/ + public static final String SCENE_DETECT_ON = "on"; + + //Values for Continuous AF + + /** @hide + * CAF off + **/ + public static final String CONTINUOUS_AF_OFF = "caf-off"; + /** @hide + * CAF on + **/ + public static final String CONTINUOUS_AF_ON = "caf-on"; + /** @hide + * Denoise off + **/ + public static final String DENOISE_OFF = "denoise-off"; + /** @hide + * Denoise on + **/ + public static final String DENOISE_ON = "denoise-on"; + + // Values for Redeye Reduction settings. + /** @hide + * REDEYE_REDUCTION_ENABLE + **/ + public static final String REDEYE_REDUCTION_ENABLE = "enable"; + /** @hide + * REDEYE_REDUCTION_DISABLE + **/ + public static final String REDEYE_REDUCTION_DISABLE = "disable"; + + // Values for selectable zone af settings. + /** @hide + * SELECTABLE_ZONE_AF_AUTO + **/ + public static final String SELECTABLE_ZONE_AF_AUTO = "auto"; + /** @hide + * SELECTABLE_ZONE_AF_SPOTMETERING + **/ + public static final String SELECTABLE_ZONE_AF_SPOTMETERING = "spot-metering"; + /** @hide + * SELECTABLE_ZONE_AF_CENTER_WEIGHTED + **/ + public static final String SELECTABLE_ZONE_AF_CENTER_WEIGHTED = "center-weighted"; + /** @hide + * SELECTABLE_ZONE_AF_FRAME_AVERAGE + **/ + public static final String SELECTABLE_ZONE_AF_FRAME_AVERAGE = "frame-average"; + + // Values for Face Detection settings. + /** @hide + * Face Detection off + **/ + public static final String FACE_DETECTION_OFF = "off"; + /** @hide + * Face Detction on + **/ + public static final String FACE_DETECTION_ON = "on"; + + // Values for video rotation settings. + + /** @hide + * VIDEO_ROTATION_0 + **/ + public static final String VIDEO_ROTATION_0 = "0"; + /** @hide + * VIDEO_ROTATION_90 + **/ + public static final String VIDEO_ROTATION_90 = "90"; + /** @hide + * VIDEO_ROTATION_180 + **/ + public static final String VIDEO_ROTATION_180 = "180"; + /** @hide + * VIDEO_ROTATION_270 + **/ + public static final String VIDEO_ROTATION_270 = "270"; + + /* ### QC ADDED PARAMETER APIS*/ + /** @hide + * Gets the supported preview sizes in high frame rate recording mode. + * + * @return a list of Size object. This method will always return a list + * with at least one element. + */ + public List<Size> getSupportedHfrSizes() { + String str = get(KEY_QC_HFR_SIZE + SUPPORTED_VALUES_SUFFIX); + return splitSize(str); + } + + /** @hide + * Gets the supported Touch AF/AEC setting. + * + * @return a List of TOUCH_AF_AEC_XXX string constants. null if TOUCH AF/AEC + * setting is not supported. + * + */ + public List<String> getSupportedTouchAfAec() { + String str = get(KEY_QC_TOUCH_AF_AEC + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** + * Gets the supported Touch AF/AEC setting. + * + * @return a List of TOUCH_AF_AEC_XXX string constants. null if TOUCH AF/AEC + * setting is not supported. + * + */ + + /** @hide + * Gets the supported frame rate modes. + * + * @return a List of FRAME_RATE_XXX_MODE string constant. null if this + * setting is not supported. + */ + public List<String> getSupportedPreviewFrameRateModes() { + String str = get(KEY_QC_PREVIEW_FRAME_RATE_MODE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported auto scene detection modes. + * + * @return a List of SCENE_DETECT_XXX string constant. null if scene detection + * setting is not supported. + * + */ + public List<String> getSupportedSceneDetectModes() { + String str = get(KEY_QC_SCENE_DETECT + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported ISO values. + * + * @return a List of FLASH_MODE_XXX string constants. null if flash mode + * setting is not supported. + */ + public List<String> getSupportedIsoValues() { + String str = get(KEY_QC_ISO_MODE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported Lensshade modes. + * + * @return a List of LENS_MODE_XXX string constants. null if lens mode + * setting is not supported. + */ + public List<String> getSupportedLensShadeModes() { + String str = get(KEY_QC_LENSSHADE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported Histogram modes. + * + * @return a List of HISTOGRAM_XXX string constants. null if histogram mode + * setting is not supported. + */ + public List<String> getSupportedHistogramModes() { + String str = get(KEY_QC_HISTOGRAM + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported Skin Tone Enhancement modes. + * + * @return a List of SKIN_TONE_ENHANCEMENT_XXX string constants. null if skin tone enhancement + * setting is not supported. + */ + public List<String> getSupportedSkinToneEnhancementModes() { + String str = get(KEY_QC_SKIN_TONE_ENHANCEMENT + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported auto exposure setting. + * + * @return a List of AUTO_EXPOSURE_XXX string constants. null if auto exposure + * setting is not supported. + */ + public List<String> getSupportedAutoexposure() { + String str = get(KEY_QC_AUTO_EXPOSURE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported MCE modes. + * + * @return a List of MCE_ENABLE/DISABLE string constants. null if MCE mode + * setting is not supported. + */ + public List<String> getSupportedMemColorEnhanceModes() { + String str = get(KEY_QC_MEMORY_COLOR_ENHANCEMENT + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported ZSL modes. + * + * @return a List of ZSL_OFF/OFF string constants. null if ZSL mode + * setting is not supported. + */ + public List<String> getSupportedZSLModes() { + String str = get(KEY_QC_ZSL + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported Video HDR modes. + * + * @return a List of Video HDR_OFF/OFF string constants. null if + * Video HDR mode setting is not supported. + */ + public List<String> getSupportedVideoHDRModes() { + String str = get(KEY_QC_VIDEO_HDR + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported HFR modes. + * + * @return a List of VIDEO_HFR_XXX string constants. null if hfr mode + * setting is not supported. + */ + public List<String> getSupportedVideoHighFrameRateModes() { + String str = get(KEY_QC_VIDEO_HIGH_FRAME_RATE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported Continuous AF modes. + * + * @return a List of CONTINUOUS_AF_XXX string constant. null if continuous AF + * setting is not supported. + * + */ + public List<String> getSupportedContinuousAfModes() { + String str = get(KEY_QC_CONTINUOUS_AF + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported DENOISE modes. + * + * @return a List of DENOISE_XXX string constant. null if DENOISE + * setting is not supported. + * + */ + public List<String> getSupportedDenoiseModes() { + String str = get(KEY_QC_DENOISE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported selectable zone af setting. + * + * @return a List of SELECTABLE_ZONE_AF_XXX string constants. null if selectable zone af + * setting is not supported. + */ + public List<String> getSupportedSelectableZoneAf() { + String str = get(KEY_QC_SELECTABLE_ZONE_AF + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported face detection modes. + * + * @return a List of FACE_DETECTION_XXX string constant. null if face detection + * setting is not supported. + * + */ + public List<String> getSupportedFaceDetectionModes() { + String str = get(KEY_QC_FACE_DETECTION + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported redeye reduction modes. + * + * @return a List of REDEYE_REDUCTION_XXX string constant. null if redeye reduction + * setting is not supported. + * + */ + public List<String> getSupportedRedeyeReductionModes() { + String str = get(KEY_QC_REDEYE_REDUCTION + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Sets GPS altitude reference. This will be stored in JPEG EXIF header. + * @param altRef reference GPS altitude in meters. + */ + public void setGpsAltitudeRef(double altRef) { + set(KEY_QC_GPS_ALTITUDE_REF, Double.toString(altRef)); + } + + /** @hide + * Sets GPS Status. This will be stored in JPEG EXIF header. + * + * @param status GPS status (UTC in seconds since January 1, + * 1970). + */ + public void setGpsStatus(double status) { + set(KEY_QC_GPS_STATUS, Double.toString(status)); + } + + /** @hide + * Sets the touch co-ordinate for Touch AEC. + * + * @param x the x co-ordinate of the touch event + * @param y the y co-ordinate of the touch event + * + */ + public void setTouchIndexAec(int x, int y) { + String v = Integer.toString(x) + "x" + Integer.toString(y); + set(KEY_QC_TOUCH_INDEX_AEC, v); + } + + /** @hide + * Returns the touch co-ordinates of the touch event. + * + * @return a Index object with the x and y co-ordinated + * for the touch event + * + */ + public Coordinate getTouchIndexAec() { + String pair = get(KEY_QC_TOUCH_INDEX_AEC); + return strToCoordinate(pair); + } + + /** @hide + * Sets the touch co-ordinate for Touch AF. + * + * @param x the x co-ordinate of the touch event + * @param y the y co-ordinate of the touch event + * + */ + public void setTouchIndexAf(int x, int y) { + String v = Integer.toString(x) + "x" + Integer.toString(y); + set(KEY_QC_TOUCH_INDEX_AF, v); + } + + /** @hide + * Returns the touch co-ordinates of the touch event. + * + * @return a Index object with the x and y co-ordinated + * for the touch event + * + */ + public Coordinate getTouchIndexAf() { + String pair = get(KEY_QC_TOUCH_INDEX_AF); + return strToCoordinate(pair); + } + /** @hide + * Set Sharpness Level + * + * @param sharpness level + */ + public void setSharpness(int sharpness){ + if((sharpness < 0) || (sharpness > getMaxSharpness()) ) + throw new IllegalArgumentException( + "Invalid Sharpness " + sharpness); + + set(KEY_QC_SHARPNESS, String.valueOf(sharpness)); + } + + /** @hide + * Set Contrast Level + * + * @param contrast level + */ + public void setContrast(int contrast){ + if((contrast < 0 ) || (contrast > getMaxContrast())) + throw new IllegalArgumentException( + "Invalid Contrast " + contrast); + + set(KEY_QC_CONTRAST, String.valueOf(contrast)); + } + + /** @hide + * Set Saturation Level + * + * @param saturation level + */ + public void setSaturation(int saturation){ + if((saturation < 0 ) || (saturation > getMaxSaturation())) + throw new IllegalArgumentException( + "Invalid Saturation " + saturation); + + set(KEY_QC_SATURATION, String.valueOf(saturation)); + } + + /** @hide + * @return true if full size video snapshot is supported. + */ + public boolean isPowerModeSupported() { + String str = get(KEY_QC_POWER_MODE_SUPPORTED); + return TRUE.equals(str); + } + + /** @hide + * Get Sharpness level + * + * @return sharpness level + */ + public int getSharpness(){ + return getInt(KEY_QC_SHARPNESS); + } + + /** @hide + * Get Max Sharpness Level + * + * @return max sharpness level + */ + public int getMaxSharpness(){ + return getInt(KEY_QC_MAX_SHARPNESS); + } + + /** @hide + * Get Contrast level + * + * @return contrast level + */ + public int getContrast(){ + return getInt(KEY_QC_CONTRAST); + } + + /** @hide + * Get Max Contrast Level + * + * @return max contrast level + */ + public int getMaxContrast(){ + return getInt(KEY_QC_MAX_CONTRAST); + } + + /** @hide + * Get Saturation level + * + * @return saturation level + */ + public int getSaturation(){ + return getInt(KEY_QC_SATURATION); + } + + /** @hide + * Get Max Saturation Level + * + * @return max contrast level + */ + public int getMaxSaturation(){ + return getInt(KEY_QC_MAX_SATURATION); + } + + /** @hide + * Sets GPS latitude reference coordinate. This will be stored in JPEG EXIF + * header. + * @param latRef GPS latitude reference coordinate. + */ + public void setGpsLatitudeRef(String latRef) { + set(KEY_QC_GPS_LATITUDE_REF, latRef); + } + + /** @hide + * Sets GPS longitude reference coordinate. This will be stored in JPEG EXIF + * header. + * @param lonRef GPS longitude reference coordinate. + */ + public void setGpsLongitudeRef(String lonRef) { + set(KEY_QC_GPS_LONGITUDE_REF, lonRef); + } + + /** @hide + * Sets system timestamp. This will be stored in JPEG EXIF header. + * + * @param dateTime current timestamp (UTC in seconds since January 1, + * 1970). + */ + public void setExifDateTime(String dateTime) { + set(KEY_QC_EXIF_DATETIME, dateTime); + } + + /** @hide + * Gets the current Touch AF/AEC setting. + * + * @return one of TOUCH_AF_AEC_XXX string constant. null if Touch AF/AEC + * setting is not supported. + * + */ + public String getTouchAfAec() { + return get(KEY_QC_TOUCH_AF_AEC); + } + + /** @hide + * Sets the current TOUCH AF/AEC setting. + * + * @param value TOUCH_AF_AEC_XXX string constants. + * + */ + public void setTouchAfAec(String value) { + set(KEY_QC_TOUCH_AF_AEC, value); + } + + /** @hide + * Gets the current redeye reduction setting. + * + * @return one of REDEYE_REDUCTION_XXX string constant. null if redeye reduction + * setting is not supported. + * + */ + public String getRedeyeReductionMode() { + return get(KEY_QC_REDEYE_REDUCTION); + } + + /** @hide + * Sets the redeye reduction. Other parameters may be changed after changing + * redeye reduction. After setting redeye reduction, + * applications should call getParameters to know if some parameters are + * changed. + * + * @param value REDEYE_REDUCTION_XXX string constants. + * + */ + public void setRedeyeReductionMode(String value) { + set(KEY_QC_REDEYE_REDUCTION, value); + } + + /** @hide + * Gets the frame rate mode setting. + * + * @return one of FRAME_RATE_XXX_MODE string constant. null if this + * setting is not supported. + */ + public String getPreviewFrameRateMode() { + return get(KEY_QC_PREVIEW_FRAME_RATE_MODE); + } + + /** @hide + * Sets the frame rate mode. + * + * @param value FRAME_RATE_XXX_MODE string constants. + */ + public void setPreviewFrameRateMode(String value) { + set(KEY_QC_PREVIEW_FRAME_RATE_MODE, value); + } + + /** @hide + * Gets the current auto scene detection setting. + * + * @return one of SCENE_DETECT_XXX string constant. null if auto scene detection + * setting is not supported. + * + */ + public String getSceneDetectMode() { + return get(KEY_QC_SCENE_DETECT); + } + + /** @hide + * Sets the auto scene detect. Other parameters may be changed after changing + * scene detect. After setting auto scene detection, + * applications should call getParameters to know if some parameters are + * changed. + * + * @param value SCENE_DETECT_XXX string constants. + * + */ + public void setSceneDetectMode(String value) { + set(KEY_QC_SCENE_DETECT, value); + } + + /** @hide + * Gets the current hdr bracketing mode setting. + * + * @return current hdr bracketing mode. + * @see #KEY_AE_BRACKET_OFF + * @see #KEY_AE_BRACKET_HDR + * @see #KEY_AE_BRACKET_BRACKATING + */ + public String getAEBracket() { + return get(KEY_QC_AE_BRACKET_HDR); + } + + /** @hide + * Sets the Power mode. + * + * @param value Power mode. + * @see #getPowerMode() + */ + public void setPowerMode(String value) { + set(KEY_QC_POWER_MODE, value); + } + + /** @hide + * Gets the current power mode setting. + * + * @return current power mode. null if power mode setting is not + * supported. + * @see #POWER_MODE_LOW + * @see #POWER_MODE_NORMAL + */ + public String getPowerMode() { + return get(KEY_QC_POWER_MODE); + } + + /** @hide + * Set HDR-Bracketing Level + * + * @param value HDR-Bracketing + */ + public void setAEBracket(String value){ + set(KEY_QC_AE_BRACKET_HDR, value); + } + + /** @hide + * Gets the current ISO setting. + * + * @return one of ISO_XXX string constant. null if ISO + * setting is not supported. + */ + public String getISOValue() { + return get(KEY_QC_ISO_MODE); + } + + /** @hide + * Sets the ISO. + * + * @param iso ISO_XXX string constant. + */ + public void setISOValue(String iso) { + set(KEY_QC_ISO_MODE, iso); + } + + /** @hide + * Sets the exposure time. + * + * @param value exposure time. + */ + public void setExposureTime(int value) { + set(KEY_QC_EXPOSURE_TIME, Integer.toString(value)); + } + + /** @hide + * Gets the current exposure time. + * + * @return exposure time. + */ + public String getExposureTime() { + return get(KEY_QC_EXPOSURE_TIME); + } + + /** @hide + * Gets the min supported exposure time. + * + * @return min supported exposure time. + */ + public String getMinExposureTime() { + return get(KEY_QC_MIN_EXPOSURE_TIME); + } + + /** @hide + * Gets the max supported exposure time. + * + * @return max supported exposure time. + */ + public String getMaxExposureTime() { + return get(KEY_QC_MAX_EXPOSURE_TIME); + } + + /** @hide + * Gets the current LensShade Mode. + * + * @return LensShade Mode + */ + public String getLensShade() { + return get(KEY_QC_LENSSHADE); + } + + /** @hide + * Sets the current LensShade Mode. + * + * @return LensShade Mode + */ + public void setLensShade(String lensshade) { + set(KEY_QC_LENSSHADE, lensshade); + } + + /** @hide + * Gets the current auto exposure setting. + * + * @return one of AUTO_EXPOSURE_XXX string constant. null if auto exposure + * setting is not supported. + */ + public String getAutoExposure() { + return get(KEY_QC_AUTO_EXPOSURE); + } + + /** @hide + * Sets the current auto exposure setting. + * + * @param value AUTO_EXPOSURE_XXX string constants. + */ + public void setAutoExposure(String value) { + set(KEY_QC_AUTO_EXPOSURE, value); + } + + /** @hide + * Gets the current MCE Mode. + * + * @return MCE value + */ + public String getMemColorEnhance() { + return get(KEY_QC_MEMORY_COLOR_ENHANCEMENT); + } + + /** @hide + * Sets the current MCE Mode. + * + * @return MCE Mode + */ + public void setMemColorEnhance(String mce) { + set(KEY_QC_MEMORY_COLOR_ENHANCEMENT, mce); + } + + /** @hide + * Set white balance manual cct value. + * + * @param cct user CCT setting. + */ + public void setWBManualCCT(int cct) { + set(KEY_QC_WB_MANUAL_CCT, Integer.toString(cct)); + } + + /** @hide + * Gets the WB min supported CCT. + * + * @return min cct value. + */ + public String getWBMinCCT() { + return get(KEY_QC_MIN_WB_CCT); + } + + /** @hide + * Gets the WB max supported CCT. + * + * @return max cct value. + */ + public String getMaxWBCCT() { + return get(KEY_QC_MAX_WB_CCT); + } + + /** @hide + * Gets the current WB CCT. + * + * @return CCT value + */ + public String getWBCurrentCCT() { + return get(KEY_QC_WB_MANUAL_CCT); + } + + /** @hide + * Gets the current ZSL Mode. + * + * @return ZSL mode value + */ + public String getZSLMode() { + return get(KEY_QC_ZSL); + } + + /** @hide + * Sets the current ZSL Mode. ZSL mode is set as a 0th bit in KEY_CAMERA_MODE. + * + * @return null + */ + public void setZSLMode(String zsl) { + set(KEY_QC_ZSL, zsl); + } + + /** @hide + * Sets the current Auto HDR Mode. + * @ auto_hdr auto hdr string for enable/disable + * @return null + */ + public void setAutoHDRMode(String auto_hdr){ + set(KEY_QC_AUTO_HDR_ENABLE,auto_hdr); + } + + /** @hide + * Gets the current Camera Mode Flag. Camera mode includes a + * flag(byte) which indicates different camera modes. + * For now support for ZSL added at bit0 + * + * @return Camera Mode. + */ + public String getCameraMode() { + return get(KEY_QC_CAMERA_MODE); + } + + /** @hide + * Sets the current Camera Mode. + * + * @return null + */ + public void setCameraMode(int cameraMode) { + set(KEY_QC_CAMERA_MODE, cameraMode); + } + + private static final int MANUAL_FOCUS_POS_TYPE_INDEX = 0; + private static final int MANUAL_FOCUS_POS_TYPE_DAC = 1; + /** @hide + * Set focus position. + * + * @param pos user setting of focus position. + */ + public void setFocusPosition(int type, int pos) { + set(KEY_QC_MANUAL_FOCUS_POS_TYPE, Integer.toString(type)); + set(KEY_QC_MANUAL_FOCUS_POSITION, Integer.toString(pos)); + } + + /** @hide + * Gets the current focus position. + * + * @return current focus position + */ + public String getCurrentFocusPosition() { + return get(KEY_QC_MANUAL_FOCUS_POSITION); + } + + + /** @hide + * Gets the current HFR Mode. + * + * @return VIDEO_HFR_XXX string constants + */ + public String getVideoHighFrameRate() { + return get(KEY_QC_VIDEO_HIGH_FRAME_RATE); + } + + /** @hide + * Sets the current HFR Mode. + * + * @param hfr VIDEO_HFR_XXX string constants + */ + public void setVideoHighFrameRate(String hfr) { + set(KEY_QC_VIDEO_HIGH_FRAME_RATE, hfr); + } + + /** @hide + * Gets the current Video HDR Mode. + * + * @return Video HDR mode value + */ + public String getVideoHDRMode() { + return get(KEY_QC_VIDEO_HDR); + } + + /** @hide + * Sets the current Video HDR Mode. + * + * @return null + */ + public void setVideoHDRMode(String videohdr) { + set(KEY_QC_VIDEO_HDR, videohdr); + } + + /** @hide + * Gets the current DENOISE setting. + * + * @return one of DENOISE_XXX string constant. null if Denoise + * setting is not supported. + * + */ + public String getDenoise() { + return get(KEY_QC_DENOISE); + } + + /** @hide + * Gets the current Continuous AF setting. + * + * @return one of CONTINUOUS_AF_XXX string constant. null if continuous AF + * setting is not supported. + * + */ + public String getContinuousAf() { + return get(KEY_QC_CONTINUOUS_AF); + } + + /** @hide + * Sets the current Denoise mode. + * @param value DENOISE_XXX string constants. + * + */ + + public void setDenoise(String value) { + set(KEY_QC_DENOISE, value); + } + + /** @hide + * Sets the current Continuous AF mode. + * @param value CONTINUOUS_AF_XXX string constants. + * + */ + public void setContinuousAf(String value) { + set(KEY_QC_CONTINUOUS_AF, value); + } + + /** @hide + * Gets the current selectable zone af setting. + * + * @return one of SELECTABLE_ZONE_AF_XXX string constant. null if selectable zone af + * setting is not supported. + */ + public String getSelectableZoneAf() { + return get(KEY_QC_SELECTABLE_ZONE_AF); + } + + /** @hide + * Sets the current selectable zone af setting. + * + * @param value SELECTABLE_ZONE_AF_XXX string constants. + */ + public void setSelectableZoneAf(String value) { + set(KEY_QC_SELECTABLE_ZONE_AF, value); + } + + /** @hide + * Gets the current face detection setting. + * + * @return one of FACE_DETECTION_XXX string constant. null if face detection + * setting is not supported. + * + */ + public String getFaceDetectionMode() { + return get(KEY_QC_FACE_DETECTION); + } + + /** @hide + * Sets the auto scene detect. Other settings like Touch AF/AEC might be + * changed after setting face detection. + * + * @param value FACE_DETECTION_XXX string constants. + * + */ + public void setFaceDetectionMode(String value) { + set(KEY_QC_FACE_DETECTION, value); + } + + /** @hide + * Gets the current video rotation setting. + * + * @return one of VIDEO_QC_ROTATION_XXX string constant. null if video rotation + * setting is not supported. + */ + public String getVideoRotation() { + return get(KEY_QC_VIDEO_ROTATION); + } + + /** @hide + * Sets the current video rotation setting. + * + * @param value VIDEO_QC_ROTATION_XXX string constants. + */ + public void setVideoRotation(String value) { + set(KEY_QC_VIDEO_ROTATION, value); + } + /** @hide + * Gets the supported video rotation modes. + * + * @return a List of VIDEO_QC_ROTATION_XXX string constant. null if this + * setting is not supported. + */ + public List<String> getSupportedVideoRotationValues() { + String str = get(KEY_QC_VIDEO_ROTATION + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + // Splits a comma delimited string to an ArrayList of Coordinate. + // Return null if the passing string is null or the Coordinate is 0. + private ArrayList<Coordinate> splitCoordinate(String str) { + if (str == null) return null; + TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(','); + splitter.setString(str); + ArrayList<Coordinate> coordinateList = new ArrayList<Coordinate>(); + for (String s : splitter) { + Coordinate coordinate = strToCoordinate(s); + if (coordinate != null) coordinateList.add(coordinate); + } + if (coordinateList.size() == 0) return null; + return coordinateList; + } + + // Parses a string (ex: "500x500") to Coordinate object. + // Return null if the passing string is null. + private Coordinate strToCoordinate(String str) { + if (str == null) return null; + + int pos = str.indexOf('x'); + if (pos != -1) { + String x = str.substring(0, pos); + String y = str.substring(pos + 1); + return new Coordinate(Integer.parseInt(x), + Integer.parseInt(y)); + } + Log.e(TAG, "Invalid Coordinate parameter string=" + str); + return null; + } + /* ### QC ADD-ONS: END */ }; } diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index 2fe8fb6..ba0d5be 100644 --- a/core/java/android/hardware/SystemSensorManager.java +++ b/core/java/android/hardware/SystemSensorManager.java @@ -101,6 +101,7 @@ public class SystemSensorManager extends SensorManager { @Override protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor, int delayUs, Handler handler, int maxBatchReportLatencyUs, int reservedFlags) { + android.util.SeempLog.record_sensor_rate(381, sensor, delayUs); if (listener == null || sensor == null) { Log.e(TAG, "sensor or listener is null"); return false; @@ -142,6 +143,7 @@ public class SystemSensorManager extends SensorManager { /** @hide */ @Override protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) { + android.util.SeempLog.record_sensor(382, sensor); // Trigger Sensors should use the cancelTriggerSensor call. if (sensor != null && sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) { return; diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index a2ef078..7464211 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -398,17 +398,24 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * this camera device.</p> * <p>For devices at the LEGACY level or above:</p> * <ul> - * <li>This list will always include (30, 30).</li> - * <li>Also, for constant-framerate recording, for each normal + * <li> + * <p>For constant-framerate recording, for each normal + * {@link android.media.CamcorderProfile CamcorderProfile}, that is, a * {@link android.media.CamcorderProfile CamcorderProfile} that has * {@link android.media.CamcorderProfile#quality quality} in * the range [{@link android.media.CamcorderProfile#QUALITY_LOW QUALITY_LOW}, * {@link android.media.CamcorderProfile#QUALITY_2160P QUALITY_2160P}], if the profile is * supported by the device and has * {@link android.media.CamcorderProfile#videoFrameRate videoFrameRate} <code>x</code>, this list will - * always include (<code>x</code>,<code>x</code>).</li> - * <li>For preview streaming use case, this list will always include (<code>min</code>, <code>max</code>) where - * <code>min</code> <= 15 and <code>max</code> >= 30.</li> + * always include (<code>x</code>,<code>x</code>).</p> + * </li> + * <li> + * <p>Also, a camera device must either not support any + * {@link android.media.CamcorderProfile CamcorderProfile}, + * or support at least one + * normal {@link android.media.CamcorderProfile CamcorderProfile} that has + * {@link android.media.CamcorderProfile#videoFrameRate videoFrameRate} <code>x</code> >= 24.</p> + * </li> * </ul> * <p>For devices at the LIMITED level or above:</p> * <ul> diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 35a1d96..f61892e 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -472,13 +472,13 @@ public abstract class CameraMetadata<TKey> { * <li>The maximum available resolution for RAW_SENSOR streams * will match either the value in * {@link CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE android.sensor.info.pixelArraySize} or - * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</li> + * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.</li> * <li>All DNG-related optional metadata entries are provided * by the camera device.</li> * </ul> * - * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE * @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE + * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES */ public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3; diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index 3f566eb..67835a0 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -556,6 +556,10 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * Set a capture request field to a value. The field definitions can be * found in {@link CaptureRequest}. * + * <p>Setting a field to {@code null} will remove that field from the capture request. + * Unless the field is optional, removing it will likely produce an error from the camera + * device when the request is submitted.</p> + * * @param key The metadata field to write. * @param value The value to set the field to, which must be of a matching * type to the key. diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java index 4866598..c26d07d 100644 --- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java +++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java @@ -81,6 +81,7 @@ public class RequestThreadManager { private static final int PREVIEW_FRAME_TIMEOUT = 1000; // ms private static final int JPEG_FRAME_TIMEOUT = 4000; // ms (same as CTS for API2) + private static final int HDR_TIMEOUT = 20000; //ms private static final int REQUEST_COMPLETE_TIMEOUT = JPEG_FRAME_TIMEOUT; private static final float ASPECT_RATIO_TOLERANCE = 0.01f; @@ -825,7 +826,9 @@ public class RequestThreadManager { if (holder.hasJpegTargets()) { doJpegCapture(holder); - if (!mReceivedJpeg.block(JPEG_FRAME_TIMEOUT)) { + if (!mReceivedJpeg.block( + mParams.getSceneMode().equals(mParams.SCENE_MODE_HDR) + ? HDR_TIMEOUT : JPEG_FRAME_TIMEOUT)) { Log.e(TAG, "Hit timeout for jpeg callback!"); mCaptureCollector.failNextJpeg(); } @@ -920,6 +923,9 @@ public class RequestThreadManager { mDeviceState.setError( CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE); } + if (mPreviewTexture != null) { + mPreviewTexture.setOnFrameAvailableListener(null); + } if (mGLThreadManager != null) { mGLThreadManager.quit(); mGLThreadManager = null; diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java index bc80fc1..86eb01d 100644 --- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java +++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java @@ -433,6 +433,20 @@ public class SurfaceTextureRenderer { EGL14.eglChooseConfig(mEGLDisplay, attribList, /*offset*/ 0, configs, /*offset*/ 0, configs.length, numConfigs, /*offset*/ 0); checkEglError("eglCreateContext RGB888+recordable ES2"); + if (numConfigs[0] == 0) { + Log.w(TAG, "eglChooseConfig returned no configs, retrying without EGL_RECORDABLE_ANDROID"); + int[] attribList2 = { + EGL14.EGL_RED_SIZE, EGL_COLOR_BITLENGTH, + EGL14.EGL_GREEN_SIZE, EGL_COLOR_BITLENGTH, + EGL14.EGL_BLUE_SIZE, EGL_COLOR_BITLENGTH, + EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT, + EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT | EGL14.EGL_WINDOW_BIT, + EGL14.EGL_NONE + }; + EGL14.eglChooseConfig(mEGLDisplay, attribList2, /*offset*/ 0, configs, /*offset*/ 0, + configs.length, numConfigs, /*offset*/ 0); + checkEglError("eglCreateContext RGB888 ES2"); + } mConfigs = configs[0]; int[] attrib_list = { EGL14.EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index ff7a300..5e9cd97 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -317,6 +317,11 @@ public class InputMethodService extends AbstractInputMethodService { final Insets mTmpInsets = new Insets(); final int[] mTmpLocation = new int[2]; + int mVolumeKeyCursorControl; + private static final int VOLUME_CURSOR_OFF = 0; + private static final int VOLUME_CURSOR_ON = 1; + private static final int VOLUME_CURSOR_ON_REVERSE = 2; + final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = new ViewTreeObserver.OnComputeInternalInsetsListener() { public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) { @@ -1856,6 +1861,26 @@ public class InputMethodService extends AbstractInputMethodService { } return false; } + if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP) { + mVolumeKeyCursorControl = Settings.System.getInt(getContentResolver(), + Settings.System.VOLUME_KEY_CURSOR_CONTROL, 0); + if (isInputViewShown() && (mVolumeKeyCursorControl != VOLUME_CURSOR_OFF)) { + sendDownUpKeyEvents((mVolumeKeyCursorControl == VOLUME_CURSOR_ON_REVERSE) + ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT); + return true; + } + return false; + } + if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN) { + mVolumeKeyCursorControl = Settings.System.getInt(getContentResolver(), + Settings.System.VOLUME_KEY_CURSOR_CONTROL, 0); + if (isInputViewShown() && (mVolumeKeyCursorControl != VOLUME_CURSOR_OFF)) { + sendDownUpKeyEvents((mVolumeKeyCursorControl == VOLUME_CURSOR_ON_REVERSE) + ? KeyEvent.KEYCODE_DPAD_LEFT : KeyEvent.KEYCODE_DPAD_RIGHT); + return true; + } + return false; + } return doMovementKey(keyCode, event, MOVEMENT_DOWN); } @@ -1906,6 +1931,15 @@ public class InputMethodService extends AbstractInputMethodService { return handleBack(true); } } + if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP + || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { + mVolumeKeyCursorControl = Settings.System.getInt(getContentResolver(), + Settings.System.VOLUME_KEY_CURSOR_CONTROL, 0); + if (isInputViewShown() && (mVolumeKeyCursorControl != VOLUME_CURSOR_OFF)) { + return true; + } + return false; + } return doMovementKey(keyCode, event, MOVEMENT_UP); } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 4d9b759..6bbd9c8 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -24,6 +24,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.net.NetworkUtils; +import android.net.wifi.WifiDevice; import android.os.Binder; import android.os.Build.VERSION_CODES; import android.os.Handler; @@ -49,6 +50,7 @@ import com.android.internal.util.Protocol; import java.net.InetAddress; import java.util.concurrent.atomic.AtomicInteger; import java.util.HashMap; +import java.util.List; import libcore.net.event.NetworkEventDispatcher; @@ -283,6 +285,15 @@ public class ConnectivityManager { "android.net.conn.TETHER_STATE_CHANGED"; /** + * Broadcast intent action indicating that a Station is connected + * or disconnected. + * + * @hide + */ + public static final String TETHER_CONNECT_STATE_CHANGED = + "codeaurora.net.conn.TETHER_CONNECT_STATE_CHANGED"; + + /** * @hide * gives a String[] listing all the interfaces configured for * tethering and currently available for tethering. @@ -1838,6 +1849,20 @@ public class ConnectivityManager { } } + /** + * Get the list of Stations connected to Hotspot. + * + * @return a list of {@link WifiDevice} objects. + * {@hide} + */ + public List<WifiDevice> getTetherConnectedSta() { + try { + return mService.getTetherConnectedSta(); + } catch (RemoteException e) { + return null; + } + } + /** {@hide} */ public static final int TETHER_ERROR_NO_ERROR = 0; /** {@hide} */ diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index d4dd669..c6de7a5 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -26,6 +26,7 @@ import android.net.NetworkQuotaInfo; import android.net.NetworkRequest; import android.net.NetworkState; import android.net.ProxyInfo; +import android.net.wifi.WifiDevice; import android.os.IBinder; import android.os.Messenger; import android.os.ParcelFileDescriptor; @@ -36,6 +37,8 @@ import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnInfo; import com.android.internal.net.VpnProfile; +import java.util.List; + /** * Interface that answers queries about, and allows changing, the * state of network connectivity. @@ -92,6 +95,8 @@ interface IConnectivityManager int setUsbTethering(boolean enable); + List<WifiDevice> getTetherConnectedSta(); + void reportInetCondition(int networkType, int percentage); void reportNetworkConnectivity(in Network network, boolean hasConnectivity); diff --git a/core/java/android/net/INetworkManagementEventObserver.aidl b/core/java/android/net/INetworkManagementEventObserver.aidl index b7af374..5a70ff1 100644 --- a/core/java/android/net/INetworkManagementEventObserver.aidl +++ b/core/java/android/net/INetworkManagementEventObserver.aidl @@ -92,6 +92,13 @@ interface INetworkManagementEventObserver { void interfaceClassDataActivityChanged(String label, boolean active, long tsNanos); /** + * Message is received from network interface. + * + * @param message The message + */ + void interfaceMessageRecevied(String message); + + /** * Information about available DNS servers has been received. * * @param iface The interface on which the information was received. diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index 7f5f377..9e639e8 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -48,6 +48,9 @@ interface INetworkPolicyManager { /** Snooze limit on policy matching given template. */ void snoozeLimit(in NetworkTemplate template); + /** Snooze warning on policy matching given template. */ + void snoozeWarning(in NetworkTemplate template); + /** Control if background data is restricted system-wide. */ void setRestrictBackground(boolean restrictBackground); boolean getRestrictBackground(); diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl index 6436e42..17033c4 100644 --- a/core/java/android/net/INetworkStatsService.aidl +++ b/core/java/android/net/INetworkStatsService.aidl @@ -57,4 +57,5 @@ interface INetworkStatsService { /** Advise persistance threshold; may be overridden internally. */ void advisePersistThreshold(long thresholdBytes); + void resetDataUsageHistoryForAllUid(in NetworkTemplate template); } diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index c4de4a2..49dc93b 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -57,6 +57,8 @@ public final class LinkProperties implements Parcelable { private int mMtu; // in the format "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max" private String mTcpBufferSizes; + private int mTcpDelayedAckSegments = 1; + private int mTcpUserCfg = 0; private static final int MIN_MTU = 68; private static final int MIN_MTU_V6 = 1280; @@ -156,6 +158,8 @@ public final class LinkProperties implements Parcelable { } setMtu(source.getMtu()); mTcpBufferSizes = source.mTcpBufferSizes; + mTcpDelayedAckSegments = source.mTcpDelayedAckSegments; + mTcpUserCfg = source.mTcpUserCfg; } } @@ -439,6 +443,45 @@ public final class LinkProperties implements Parcelable { return mTcpBufferSizes; } + /** + * Number of full MSS to receive before Acking RFC2581 + * @param segments The number of segments to receive + * + * @hide + */ + public void setTcpDelayedAckSegments(int segments) { + mTcpDelayedAckSegments = segments; + } + + /** + * Gets the number of segments before acking + * + * @hide + */ + public int getTcpDelayedAckSegments() { + return mTcpDelayedAckSegments; + } + + /** + * Sets the value for TCP usercfg + * + * @param value 0/1 currently to disable/enable + * + * @hide + */ + public void setTcpUserCfg(int value) { + mTcpUserCfg = value; + } + + /** + * Gets the value of TCP usercfg + * + * @hide + */ + public int getTcpUserCfg() { + return mTcpUserCfg; + } + private RouteInfo routeWithInterface(RouteInfo route) { return new RouteInfo( route.getDestination(), @@ -599,6 +642,8 @@ public final class LinkProperties implements Parcelable { mStackedLinks.clear(); mMtu = 0; mTcpBufferSizes = null; + mTcpDelayedAckSegments = 1; + mTcpUserCfg = 0; } /** diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index a83e722..7f4d6e3 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.GET_SIGNATURES; import static android.net.NetworkPolicy.CYCLE_NONE; import static android.text.format.Time.MONTH_DAY; +import android.annotation.SystemApi; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; @@ -48,6 +49,12 @@ public class NetworkPolicyManager { public static final int POLICY_REJECT_METERED_BACKGROUND = 0x1; /** Allow network use (metered or not) in the background in battery save mode. */ public static final int POLICY_ALLOW_BACKGROUND_BATTERY_SAVE = 0x2; + /** Reject application network traffic on wifi network **/ + public static final int POLICY_REJECT_ON_WLAN = 0x8000; + /** Reject application network traffic on cellular network **/ + public static final int POLICY_REJECT_ON_DATA = 0x10000; + /** Reject application background network traffic on WiFi network **/ + public static final int POLICY_REJECT_ON_WLAN_BACKGROUND = 0x20000; /* RULE_* are not masks and they must be exclusive */ public static final int RULE_UNKNOWN = -1; @@ -81,6 +88,54 @@ public class NetworkPolicyManager { */ public static final String EXTRA_NETWORK_TEMPLATE = "android.net.NETWORK_TEMPLATE"; + /** + * Broadcast intent action for informing a custom component about a network policy + * notification. + * @hide + */ + @SystemApi + public static final String ACTION_SHOW_NETWORK_POLICY_NOTIFICATION = + "android.net.action.SHOW_NETWORK_POLICY_NOTIFICATION"; + + /** + * The sequence number associated with the notification - a higher number + * indicates previous notifications may be disregarded. + * @hide + */ + @SystemApi + public static final String EXTRA_NOTIFICATION_SEQUENCE_NUMBER = + "android.net.extra.NOTIFICATION_SEQUENCE_NUMBER"; + + /** + * The type of notification that should be presented to the user. + * @hide + */ + @SystemApi + public static final String EXTRA_NOTIFICATION_TYPE = "android.net.extra.NOTIFICATION_TYPE"; + + @SystemApi + public static final int NOTIFICATION_TYPE_NONE = 0; + @SystemApi + public static final int NOTIFICATION_TYPE_USAGE_WARNING = 1; + @SystemApi + public static final int NOTIFICATION_TYPE_USAGE_REACHED_LIMIT = 2; + @SystemApi + public static final int NOTIFICATION_TYPE_USAGE_EXCEEDED_LIMIT = 3; + + /** + * The number of bytes used on the network in the notification. + * @hide + */ + @SystemApi + public static final String EXTRA_BYTES_USED = "android.net.extra.BYTES_USED"; + + /** + * The network policy for the network in the notification. + * @hide + */ + @SystemApi + public static final String EXTRA_NETWORK_POLICY = "android.net.extra.NETWORK_POLICY"; + private final Context mContext; private INetworkPolicyManager mService; diff --git a/core/java/android/net/wimax/WimaxHelper.java b/core/java/android/net/wimax/WimaxHelper.java new file mode 100644 index 0000000..9a6727e --- /dev/null +++ b/core/java/android/net/wimax/WimaxHelper.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2011-2015 The CyanogenMod 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.net.wimax; + +import dalvik.system.DexClassLoader; + +import android.content.Context; +import android.content.ContextWrapper; +import android.os.Handler; +import android.os.IBinder; +import android.os.ServiceManager; +import android.util.Log; +import android.provider.Settings; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +/** + * {@hide} + */ +public class WimaxHelper { + + private static final String TAG = "WimaxHelper"; + + private static final String WIMAX_CONTROLLER_CLASSNAME = "com.htc.net.wimax.WimaxController"; + private static final String WIMAX_MANAGER_CLASSNAME = "android.net.fourG.wimax.Wimax4GManager"; + + private static DexClassLoader sWimaxClassLoader; + private static String sWimaxManagerClassname, sIsWimaxEnabledMethodname, + sSetWimaxEnabledMethodname, sGetWimaxStateMethodname; + + public static boolean isWimaxSupported(Context context) { + return context.getResources().getBoolean( + com.android.internal.R.bool.config_wimaxEnabled); + } + + public static DexClassLoader getWimaxClassLoader(Context context) { + if (isWimaxSupported(context)) { + if (sWimaxClassLoader == null) { + sWimaxManagerClassname = context.getResources().getString( + com.android.internal.R.string.config_wimaxManagerClassname); + + // WimaxController::getWimaxState == Wimax4GManager::get4GState. + // However, Wimax4GManager also implements a different getWimaxState + // method, which returns a WimaxState object describing the connection + // state, not the enabled state. Other methods are similarly renamed. + if (sWimaxManagerClassname.equals(WIMAX_CONTROLLER_CLASSNAME)) { + sIsWimaxEnabledMethodname = "isWimaxEnabled"; + sSetWimaxEnabledMethodname = "setWimaxEnabled"; + sGetWimaxStateMethodname = "getWimaxState"; + } else if (sWimaxManagerClassname.equals(WIMAX_MANAGER_CLASSNAME)) { + sIsWimaxEnabledMethodname = "is4GEnabled"; + sSetWimaxEnabledMethodname = "set4GEnabled"; + sGetWimaxStateMethodname = "get4GState"; + } + + String wimaxJarLocation = context.getResources().getString( + com.android.internal.R.string.config_wimaxServiceJarLocation); + String wimaxLibLocation = context.getResources().getString( + com.android.internal.R.string.config_wimaxNativeLibLocation); + sWimaxClassLoader = new DexClassLoader(wimaxJarLocation, + new ContextWrapper(context).getCacheDir().getAbsolutePath(), + wimaxLibLocation,ClassLoader.getSystemClassLoader()); + } + return sWimaxClassLoader; + } + return null; + } + + public static Object createWimaxService(Context context, Handler handler) { + Object controller = null; + + try { + DexClassLoader wimaxClassLoader = getWimaxClassLoader(context); + if (sWimaxManagerClassname.equals(WIMAX_CONTROLLER_CLASSNAME)) { + // Load supersonic's and speedy's WimaxController. + IBinder b = ServiceManager.getService(WimaxManagerConstants.WIMAX_SERVICE); + if (b != null) { + Class<?> klass = wimaxClassLoader.loadClass("com.htc.net.wimax.IWimaxController$Stub"); + if (klass != null) { + Method asInterface = klass.getMethod("asInterface", IBinder.class); + Object wc = asInterface.invoke(null, b); + if (wc != null) { + klass = wimaxClassLoader.loadClass(WIMAX_CONTROLLER_CLASSNAME); + if (klass != null) { + Constructor<?> ctor = klass.getDeclaredConstructors()[1]; + controller = ctor.newInstance(wc, handler); + } + } + } + } + } else if (sWimaxManagerClassname.equals(WIMAX_MANAGER_CLASSNAME)) { + // Load crespo4g's (and epicmtd's) Wimax4GManager. + // Note that crespo4g's implementation grabs WIMAX_SERVICE internally, so + // it doesn't need to be passed in. Other implementations (may) require + // WIMAX_SERVICE to be grabbed externally, so check Wimax4GManager::<init>. + Class<?> klass = wimaxClassLoader.loadClass(WIMAX_MANAGER_CLASSNAME); + if (klass != null) { + Constructor<?> ctor = klass.getDeclaredConstructors()[0]; + controller = ctor.newInstance(); + } + } + } catch (Exception e) { + Log.e(TAG, "Unable to create WimaxController instance", e); + } + + return controller; + } + + public static boolean isWimaxEnabled(Context context) { + boolean ret = false; + try { + Object wimaxService = context.getSystemService(WimaxManagerConstants.WIMAX_SERVICE); + Method m = wimaxService.getClass().getMethod(sIsWimaxEnabledMethodname); + ret = (Boolean) m.invoke(wimaxService); + } catch (Exception e) { + Log.e(TAG, "Unable to get WiMAX enabled state!", e); + } + return ret; + } + + public static boolean setWimaxEnabled(Context context, boolean enabled) { + boolean ret = false; + try { + Object wimaxService = context.getSystemService(WimaxManagerConstants.WIMAX_SERVICE); + Method m = wimaxService.getClass().getMethod(sSetWimaxEnabledMethodname, boolean.class); + ret = (Boolean) m.invoke(wimaxService, enabled); + if (ret) + Settings.Secure.putInt(context.getContentResolver(), + Settings.Secure.WIMAX_ON, (Boolean) enabled ? 1 : 0); + } catch (Exception e) { + Log.e(TAG, "Unable to set WiMAX state!", e); + } + return ret; + } + + public static int getWimaxState(Context context) { + int ret = 0; + try { + Object wimaxService = context.getSystemService(WimaxManagerConstants.WIMAX_SERVICE); + Method m = wimaxService.getClass().getMethod(sGetWimaxStateMethodname); + ret = (Integer) m.invoke(wimaxService); + } catch (Exception e) { + Log.e(TAG, "Unable to get WiMAX state!", e); + } + return ret; + } + + public static boolean wimaxRescan(Context context) { + boolean ret = false; + try { + Object wimaxService = context.getSystemService(WimaxManagerConstants.WIMAX_SERVICE); + Method wimaxRescan = wimaxService.getClass().getMethod("wimaxRescan"); + if (wimaxRescan != null) { + wimaxRescan.invoke(wimaxService); + ret = true; + } + } catch (Exception e) { + Log.e(TAG, "Unable to perform WiMAX rescan!", e); + } + return ret; + } + + private static Object getWimaxInfo(Context context) { + Object wimaxInfo = null; + try { + Object wimaxService = context.getSystemService(WimaxManagerConstants.WIMAX_SERVICE); + Method getConnectionInfo = wimaxService.getClass().getMethod("getConnectionInfo"); + wimaxInfo = getConnectionInfo.invoke(wimaxService); + } catch (Exception e) { + Log.e(TAG, "Unable to get a WimaxInfo object!", e); + } + return wimaxInfo; + } +}
\ No newline at end of file diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl index 961a3f4..0107d93 100644 --- a/core/java/android/nfc/INfcAdapter.aidl +++ b/core/java/android/nfc/INfcAdapter.aidl @@ -1,4 +1,7 @@ /* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * Not a Contribution. + * * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,6 +31,7 @@ import android.nfc.INfcTag; import android.nfc.INfcCardEmulation; import android.nfc.INfcUnlockHandler; import android.os.Bundle; +import android.os.IBinder; /** * @hide @@ -37,6 +41,7 @@ interface INfcAdapter INfcTag getNfcTagInterface(); INfcCardEmulation getNfcCardEmulationInterface(); INfcAdapterExtras getNfcAdapterExtrasInterface(in String pkg); + IBinder getNfcAdapterVendorInterface(in String vendor); int getState(); boolean disable(boolean saveState); diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java index d619c0a..c7d4c65 100644 --- a/core/java/android/nfc/NfcActivityManager.java +++ b/core/java/android/nfc/NfcActivityManager.java @@ -371,40 +371,44 @@ public final class NfcActivityManager extends IAppCallback.Stub flags = state.flags; activity = state.activity; } - - // Make callbacks without lock - if (ndefCallback != null) { - message = ndefCallback.createNdefMessage(event); - } - if (urisCallback != null) { - uris = urisCallback.createBeamUris(event); - if (uris != null) { - ArrayList<Uri> validUris = new ArrayList<Uri>(); - for (Uri uri : uris) { - if (uri == null) { - Log.e(TAG, "Uri not allowed to be null."); - continue; - } - String scheme = uri.getScheme(); - if (scheme == null || (!scheme.equalsIgnoreCase("file") && - !scheme.equalsIgnoreCase("content"))) { - Log.e(TAG, "Uri needs to have " + - "either scheme file or scheme content"); - continue; + final long ident = Binder.clearCallingIdentity(); + try { + // Make callbacks without lock + if (ndefCallback != null) { + message = ndefCallback.createNdefMessage(event); + } + if (urisCallback != null) { + uris = urisCallback.createBeamUris(event); + if (uris != null) { + ArrayList<Uri> validUris = new ArrayList<Uri>(); + for (Uri uri : uris) { + if (uri == null) { + Log.e(TAG, "Uri not allowed to be null."); + continue; + } + String scheme = uri.getScheme(); + if (scheme == null || (!scheme.equalsIgnoreCase("file") && + !scheme.equalsIgnoreCase("content"))) { + Log.e(TAG, "Uri needs to have " + + "either scheme file or scheme content"); + continue; + } + uri = ContentProvider.maybeAddUserId(uri, UserHandle.myUserId()); + validUris.add(uri); } - uri = ContentProvider.maybeAddUserId(uri, UserHandle.myUserId()); - validUris.add(uri); - } - uris = validUris.toArray(new Uri[validUris.size()]); + uris = validUris.toArray(new Uri[validUris.size()]); + } } - } - if (uris != null && uris.length > 0) { - for (Uri uri : uris) { - // Grant the NFC process permission to read these URIs - activity.grantUriPermission("com.android.nfc", uri, - Intent.FLAG_GRANT_READ_URI_PERMISSION); + if (uris != null && uris.length > 0) { + for (Uri uri : uris) { + // Grant the NFC process permission to read these URIs + activity.grantUriPermission("com.android.nfc", uri, + Intent.FLAG_GRANT_READ_URI_PERMISSION); + } } + } finally { + Binder.restoreCallingIdentity(ident); } return new BeamShareData(message, uris, new UserHandle(UserHandle.myUserId()), flags); } diff --git a/core/java/android/nfc/cardemulation/AidGroup.java b/core/java/android/nfc/cardemulation/AidGroup.java index 78a9401..9abf325 100644 --- a/core/java/android/nfc/cardemulation/AidGroup.java +++ b/core/java/android/nfc/cardemulation/AidGroup.java @@ -1,6 +1,9 @@ /* * Copyright (C) 2015 The Android Open Source Project * + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * Not a Contribution. + * * 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 @@ -37,7 +40,7 @@ import android.util.Log; * * @hide */ -public final class AidGroup implements Parcelable { +public class AidGroup implements Parcelable { /** * The maximum number of AIDs that can be present in any one group. */ @@ -45,9 +48,9 @@ public final class AidGroup implements Parcelable { static final String TAG = "AidGroup"; - final List<String> aids; - final String category; - final String description; + protected List<String> aids; + protected String category; + protected String description; /** * Creates a new AidGroup object. diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java index 7678678..09487d7 100644 --- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -48,53 +48,53 @@ import java.util.Map; /** * @hide */ -public final class ApduServiceInfo implements Parcelable { +public class ApduServiceInfo implements Parcelable { static final String TAG = "ApduServiceInfo"; /** * The service that implements this */ - final ResolveInfo mService; + protected ResolveInfo mService; /** * Description of the service */ - final String mDescription; + protected String mDescription; /** * Whether this service represents AIDs running on the host CPU */ - final boolean mOnHost; + protected boolean mOnHost; /** * Mapping from category to static AID group */ - final HashMap<String, AidGroup> mStaticAidGroups; + protected HashMap<String, AidGroup> mStaticAidGroups; /** * Mapping from category to dynamic AID group */ - final HashMap<String, AidGroup> mDynamicAidGroups; + protected HashMap<String, AidGroup> mDynamicAidGroups; /** * Whether this service should only be started when the device is unlocked. */ - final boolean mRequiresDeviceUnlock; + protected boolean mRequiresDeviceUnlock; /** * The id of the service banner specified in XML. */ - final int mBannerResourceId; + protected int mBannerResourceId; /** * The uid of the package the service belongs to */ - final int mUid; + protected int mUid; /** * Settings Activity for this service */ - final String mSettingsActivityName; + protected String mSettingsActivityName; /** * @hide diff --git a/core/java/android/nfc/tech/MifareClassic.java b/core/java/android/nfc/tech/MifareClassic.java index 8c92288..302c02d 100644 --- a/core/java/android/nfc/tech/MifareClassic.java +++ b/core/java/android/nfc/tech/MifareClassic.java @@ -1,4 +1,6 @@ /* + * Copyright (C) 2015 NXP Semiconductors + * The original Work has been changed by NXP Semiconductors. * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -173,6 +175,10 @@ public final class MifareClassic extends BasicTagTechnology { mType = TYPE_CLASSIC; mSize = SIZE_4K; break; + case 0x19: + mType = TYPE_CLASSIC; + mSize = SIZE_2K; + break; case 0x28: mType = TYPE_CLASSIC; mSize = SIZE_1K; diff --git a/core/java/android/nfc/tech/NfcA.java b/core/java/android/nfc/tech/NfcA.java index 88730f9..b7fa455 100644 --- a/core/java/android/nfc/tech/NfcA.java +++ b/core/java/android/nfc/tech/NfcA.java @@ -1,4 +1,6 @@ /* + * Copyright (C) 2015 NXP Semiconductors + * The original Work has been changed by NXP Semiconductors. * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -66,8 +68,15 @@ public final class NfcA extends BasicTagTechnology { /** @hide */ public NfcA(Tag tag) throws RemoteException { super(tag, TagTechnology.NFC_A); - Bundle extras = tag.getTechExtras(TagTechnology.NFC_A); - mSak = extras.getShort(EXTRA_SAK); + Bundle extras; + mSak = 0; + if(tag.hasTech(TagTechnology.MIFARE_CLASSIC)) + { + extras = tag.getTechExtras(TagTechnology.MIFARE_CLASSIC); + mSak = extras.getShort(EXTRA_SAK); + } + extras = tag.getTechExtras(TagTechnology.NFC_A); + mSak |= extras.getShort(EXTRA_SAK); mAtqa = extras.getByteArray(EXTRA_ATQA); } diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java index 1f3e9a7..050820c 100644 --- a/core/java/android/os/BatteryManager.java +++ b/core/java/android/os/BatteryManager.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2016 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +17,7 @@ package android.os; +import android.app.IBatteryService; import android.content.Context; import android.os.BatteryProperty; import android.os.IBatteryPropertiesRegistrar; @@ -95,6 +97,79 @@ public class BatteryManager { /** * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * integer containing the current dock status constant. + * @hide + */ + public static final String EXTRA_DOCK_STATUS = "dock_status"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * integer containing the current dock health constant. + * @hide + */ + public static final String EXTRA_DOCK_HEALTH = "dock_health"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * boolean indicating whether a dock battery is present. + * @hide + */ + public static final String EXTRA_DOCK_PRESENT = "dock_present"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * integer field containing the current dock battery level, from 0 to + * {@link #EXTRA_SCALE}. + * @hide + */ + public static final String EXTRA_DOCK_LEVEL = "dock_level"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * integer containing the maximum dock battery level. + * @hide + */ + public static final String EXTRA_DOCK_SCALE = "dock_scale"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * integer containing the resource ID of a small status bar icon + * indicating the current dock battery state. + * @hide + */ + public static final String EXTRA_DOCK_ICON_SMALL = "dock_icon-small"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * integer indicating whether the device is plugged in to a dock power + * source. + * @hide + */ + public static final String EXTRA_DOCK_PLUGGED = "dock_plugged"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * integer containing the current dock battery voltage level. + * @hide + */ + public static final String EXTRA_DOCK_VOLTAGE = "dock_voltage"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * integer containing the current dock battery temperature. + * @hide + */ + public static final String EXTRA_DOCK_TEMPERATURE = "dock_temperature"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * String describing the technology of the current dock battery. + * @hide + */ + public static final String EXTRA_DOCK_TECHNOLOGY = "dock_technology"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: * Int value set to nonzero if an unsupported charger is attached * to the device. * {@hide} @@ -133,10 +208,23 @@ public class BatteryManager { /** Power source is wireless. */ public static final int BATTERY_PLUGGED_WIRELESS = 4; + // values of the "dock_plugged" field in the ACTION_BATTERY_CHANGED intent. + // These must be powers of 2. + /** Power source is an DockAC charger. + * @hide*/ + public static final int BATTERY_DOCK_PLUGGED_AC = 1; + /** Power source is an DockUSB charger. + * @hide*/ + public static final int BATTERY_DOCK_PLUGGED_USB = 2; + /** @hide */ public static final int BATTERY_PLUGGED_ANY = BATTERY_PLUGGED_AC | BATTERY_PLUGGED_USB | BATTERY_PLUGGED_WIRELESS; + /** @hide */ + public static final int BATTERY_DOCK_PLUGGED_ANY = + BATTERY_DOCK_PLUGGED_AC | BATTERY_DOCK_PLUGGED_USB; + /** * Sent when the device's battery has started charging (or has reached full charge * and the device is on power). This is a good time to do work that you would like to @@ -191,6 +279,7 @@ public class BatteryManager { */ public static final int BATTERY_PROPERTY_ENERGY_COUNTER = 5; + private final IBatteryService mBatteryService; private final IBatteryStats mBatteryStats; private final IBatteryPropertiesRegistrar mBatteryPropertiesRegistrar; @@ -202,6 +291,27 @@ public class BatteryManager { ServiceManager.getService(BatteryStats.SERVICE_NAME)); mBatteryPropertiesRegistrar = IBatteryPropertiesRegistrar.Stub.asInterface( ServiceManager.getService("batteryproperties")); + mBatteryService = null; + } + + /** @hide */ + public BatteryManager(IBatteryService service) { + super(); + mBatteryStats = IBatteryStats.Stub.asInterface( + ServiceManager.getService(BatteryStats.SERVICE_NAME)); + mBatteryPropertiesRegistrar = IBatteryPropertiesRegistrar.Stub.asInterface( + ServiceManager.getService("batteryproperties")); + mBatteryService = service; + } + + /** @hide */ + public boolean isDockBatterySupported() { + try { + return mBatteryService != null && mBatteryService.isDockBatterySupported(); + } catch (RemoteException ex) { + // Ignore + } + return false; } /** @@ -223,8 +333,10 @@ public class BatteryManager { * * Returns the requested value, or Long.MIN_VALUE if property not * supported on this system or on other error. + * fromDock determines if the property is query from the normal battery + * or the dock battery. */ - private long queryProperty(int id) { + private long queryProperty(int id, boolean fromDock) { long ret; if (mBatteryPropertiesRegistrar == null) { @@ -234,7 +346,13 @@ public class BatteryManager { try { BatteryProperty prop = new BatteryProperty(); - if (mBatteryPropertiesRegistrar.getProperty(id, prop) == 0) + final int callResult; + if (!fromDock) { + callResult = mBatteryPropertiesRegistrar.getProperty(id, prop); + } else { + callResult = mBatteryPropertiesRegistrar.getDockProperty(id, prop); + } + if (callResult == 0) ret = prop.getLong(); else ret = Long.MIN_VALUE; @@ -255,7 +373,7 @@ public class BatteryManager { * @return the property value, or Integer.MIN_VALUE if not supported. */ public int getIntProperty(int id) { - return (int)queryProperty(id); + return (int)queryProperty(id, false); } /** @@ -268,6 +386,40 @@ public class BatteryManager { * @return the property value, or Long.MIN_VALUE if not supported. */ public long getLongProperty(int id) { - return queryProperty(id); + return queryProperty(id, false); + } + + /** + * Return the value of a dock battery property of integer type. If the + * platform does not provide the property queried, this value will + * be Integer.MIN_VALUE. + * + * @param id identifier of the requested property + * + * @return the property value, or Integer.MIN_VALUE if not supported. + * @hide + */ + public int getIntDockProperty(int id) { + if (!isDockBatterySupported()) { + return Integer.MIN_VALUE; + } + return (int)queryProperty(id, true); + } + + /** + * Return the value of a dock battery property of long type If the + * platform does not provide the property queried, this value will + * be Long.MIN_VALUE. + * + * @param id identifier of the requested property + * + * @return the property value, or Long.MIN_VALUE if not supported. + * @hide + */ + public long getLongDockProperty(int id) { + if (!isDockBatterySupported()) { + return Long.MIN_VALUE; + } + return queryProperty(id, true); } } diff --git a/core/java/android/os/BatteryManagerInternal.java b/core/java/android/os/BatteryManagerInternal.java index f3a95b9..1abb3d5 100644 --- a/core/java/android/os/BatteryManagerInternal.java +++ b/core/java/android/os/BatteryManagerInternal.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 The Android Open Source Project + * Copyright (C) 2016 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,6 +44,26 @@ public abstract class BatteryManagerInternal { public abstract boolean getBatteryLevelLow(); /** + * Returns whether dock batteries is supported + */ + public abstract boolean isDockBatterySupported(); + + /** + * Returns the current dock plug type. + */ + public abstract int getDockPlugType(); + + /** + * Returns dock battery level as a percentage. + */ + public abstract int getDockBatteryLevel(); + + /** + * Returns whether we currently consider the dock battery level to be low. + */ + public abstract boolean getDockBatteryLevelLow(); + + /** * Returns a non-zero value if an unsupported charger is attached. */ public abstract int getInvalidCharger(); diff --git a/core/java/android/os/BatteryProperties.java b/core/java/android/os/BatteryProperties.java index 29e868c..cad741a 100644 --- a/core/java/android/os/BatteryProperties.java +++ b/core/java/android/os/BatteryProperties.java @@ -1,4 +1,5 @@ /* Copyright 2013, The Android Open Source Project + * Copyright 2016, The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,6 +32,16 @@ public class BatteryProperties implements Parcelable { public int batteryTemperature; public String batteryTechnology; + public boolean dockBatterySupported; + public boolean chargerDockAcOnline; + public int dockBatteryStatus; + public int dockBatteryHealth; + public boolean dockBatteryPresent; + public int dockBatteryLevel; + public int dockBatteryVoltage; + public int dockBatteryTemperature; + public String dockBatteryTechnology; + public BatteryProperties() { } @@ -46,6 +57,16 @@ public class BatteryProperties implements Parcelable { batteryVoltage = other.batteryVoltage; batteryTemperature = other.batteryTemperature; batteryTechnology = other.batteryTechnology; + + dockBatterySupported = other.dockBatterySupported; + chargerDockAcOnline = other.chargerDockAcOnline; + dockBatteryStatus = other.dockBatteryStatus; + dockBatteryHealth = other.dockBatteryHealth; + dockBatteryPresent = other.dockBatteryPresent; + dockBatteryLevel = other.dockBatteryLevel; + dockBatteryVoltage = other.dockBatteryVoltage; + dockBatteryTemperature = other.dockBatteryTemperature; + dockBatteryTechnology = other.dockBatteryTechnology; } /* @@ -65,6 +86,27 @@ public class BatteryProperties implements Parcelable { batteryVoltage = p.readInt(); batteryTemperature = p.readInt(); batteryTechnology = p.readString(); + + dockBatterySupported = p.readInt() == 1 ? true : false; + if (dockBatterySupported) { + chargerDockAcOnline = p.readInt() == 1 ? true : false; + dockBatteryStatus = p.readInt(); + dockBatteryHealth = p.readInt(); + dockBatteryPresent = p.readInt() == 1 ? true : false; + dockBatteryLevel = p.readInt(); + dockBatteryVoltage = p.readInt(); + dockBatteryTemperature = p.readInt(); + dockBatteryTechnology = p.readString(); + } else { + chargerDockAcOnline = false; + dockBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN; + dockBatteryHealth = BatteryManager.BATTERY_HEALTH_UNKNOWN; + dockBatteryPresent = false; + dockBatteryLevel = 0; + dockBatteryVoltage = 0; + dockBatteryTemperature = 0; + dockBatteryTechnology = ""; + } } public void writeToParcel(Parcel p, int flags) { @@ -79,6 +121,18 @@ public class BatteryProperties implements Parcelable { p.writeInt(batteryVoltage); p.writeInt(batteryTemperature); p.writeString(batteryTechnology); + + p.writeInt(dockBatterySupported ? 1 : 0); + if (dockBatterySupported) { + p.writeInt(chargerDockAcOnline ? 1 : 0); + p.writeInt(dockBatteryStatus); + p.writeInt(dockBatteryHealth); + p.writeInt(dockBatteryPresent ? 1 : 0); + p.writeInt(dockBatteryLevel); + p.writeInt(dockBatteryVoltage); + p.writeInt(dockBatteryTemperature); + p.writeString(dockBatteryTechnology); + } } public static final Parcelable.Creator<BatteryProperties> CREATOR diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index f346fe7..a800ed6 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -37,6 +37,7 @@ public class Environment { private static final String ENV_ANDROID_STORAGE = "ANDROID_STORAGE"; private static final String ENV_OEM_ROOT = "OEM_ROOT"; private static final String ENV_VENDOR_ROOT = "VENDOR_ROOT"; + private static final String ENV_PREBUNDLED_ROOT = "PREBUNDLED_ROOT"; /** {@hide} */ public static final String DIR_ANDROID = "Android"; @@ -55,6 +56,7 @@ public class Environment { private static final File DIR_ANDROID_STORAGE = getDirectory(ENV_ANDROID_STORAGE, "/storage"); private static final File DIR_OEM_ROOT = getDirectory(ENV_OEM_ROOT, "/oem"); private static final File DIR_VENDOR_ROOT = getDirectory(ENV_VENDOR_ROOT, "/vendor"); + private static final File DIR_PREBUNDLED_ROOT = getDirectory(ENV_PREBUNDLED_ROOT, "/bundled-app"); private static final String SYSTEM_PROPERTY_EFS_ENABLED = "persist.security.efs.enabled"; @@ -165,6 +167,15 @@ public class Environment { } /** + * Return the root directory for "prebundled" apps. These apps will be installed directly + * from this partition but will not be marked as system apps and will hence be uninstallable. + * @hide + */ + public static File getPrebundledDirectory() { + return DIR_PREBUNDLED_ROOT; + } + + /** * Gets the system directory available for secure storage. * If Encrypted File system is enabled, it returns an encrypted directory (/data/secure/system). * Otherwise, it returns the unencrypted /data/system directory. diff --git a/core/java/android/os/IBatteryPropertiesRegistrar.aidl b/core/java/android/os/IBatteryPropertiesRegistrar.aidl index fd01802..43b9650 100644 --- a/core/java/android/os/IBatteryPropertiesRegistrar.aidl +++ b/core/java/android/os/IBatteryPropertiesRegistrar.aidl @@ -1,5 +1,6 @@ /* ** Copyright 2013, The Android Open Source Project +** Copyright 2016, The CyanogenMod Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -27,4 +28,5 @@ interface IBatteryPropertiesRegistrar { void registerListener(IBatteryPropertiesListener listener); void unregisterListener(IBatteryPropertiesListener listener); int getProperty(in int id, out BatteryProperty prop); + int getDockProperty(in int id, out BatteryProperty prop); } diff --git a/core/java/android/os/IDeviceIdleController.aidl b/core/java/android/os/IDeviceIdleController.aidl index f55883a..6c7cd6d 100644 --- a/core/java/android/os/IDeviceIdleController.aidl +++ b/core/java/android/os/IDeviceIdleController.aidl @@ -35,4 +35,5 @@ interface IDeviceIdleController { long addPowerSaveTempWhitelistAppForMms(String name, int userId, String reason); long addPowerSaveTempWhitelistAppForSms(String name, int userId, String reason); void exitIdle(String reason); + int getIdleStateDetailed(); } diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index cd84c8f..aa3921a 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -440,4 +440,7 @@ interface INetworkManagementService void addInterfaceToLocalNetwork(String iface, in List<RouteInfo> routes); void removeInterfaceFromLocalNetwork(String iface); + + void restrictAppOnData(int uid, boolean restrict); + void restrictAppOnWlan(int uid, boolean restrict); } diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 0f37ac7..4e0f329 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -60,4 +60,14 @@ interface IPowerManager // sets the attention light (used by phone app only) void setAttentionLight(boolean on, int color); + // update the uids being synchronized by network socket request manager + void updateBlockedUids(int uid, boolean isBlocked); + + void setKeyboardVisibility(boolean visible); + + void setKeyboardLight(boolean on, int key); + + oneway void cpuBoost(int duration); + + void wakeUpWithProximityCheck(long time, String reason, String opPackageName); } diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 9a1a03e..bbee5a3 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -18,7 +18,10 @@ package android.os; import android.annotation.SdkConstant; import android.annotation.SystemApi; +import android.content.ComponentName; import android.content.Context; +import android.content.Intent; +import android.text.TextUtils; import android.util.Log; /** @@ -387,7 +390,7 @@ public final class PowerManager { * @hide */ public static final String REBOOT_RECOVERY = "recovery"; - + final Context mContext; final IPowerManager mService; final Handler mHandler; @@ -674,6 +677,19 @@ public final class PowerManager { } /** + * Forces the device to wake up from sleep only if + * nothing is blocking the proximity sensor + * @see #wakeUp + * @hide + */ + public void wakeUpWithProximityCheck(long time, String reason) { + try { + mService.wakeUpWithProximityCheck(time, reason, mContext.getOpPackageName()); + } catch (RemoteException e) { + } + } + + /** * Forces the device to start napping. * <p> * If the device is currently awake, starts dreaming, otherwise does nothing. @@ -1235,4 +1251,68 @@ public final class PowerManager { } } } + + /** + * @hide + */ + public void setKeyboardVisibility(boolean visible) + { + try { + if (mService != null) { + mService.setKeyboardVisibility(visible); + } + } catch (RemoteException e) { + } + } + + /** + * sets the keyboard LED state + * + * @param on boolean state + * @param key 1 for caps, 2 for fn + * + * {@hide} + */ + public void setKeyboardLight(boolean on, int key) + { + try { + mService.setKeyboardLight(on, key); + } catch (RemoteException e) { + } + } + + /** + * Gets the default button brightness value. + * @hide + */ + public int getDefaultButtonBrightness() { + return mContext.getResources().getInteger( + com.android.internal.R.integer.config_buttonBrightnessSettingDefault); + } + + /** + * Gets the default keyboard brightness value. + * @hide + */ + public int getDefaultKeyboardBrightness() { + return mContext.getResources().getInteger( + com.android.internal.R.integer.config_keyboardBrightnessSettingDefault); + } + + /** + * Boost the CPU. Boosts the cpu for the given duration in microseconds. + * + * @param duration in microseconds to boost the CPU + * + * @hide + */ + public void cpuBoost(int duration) + { + try { + if (mService != null) { + mService.cpuBoost(duration); + } + } catch (RemoteException e) { + } + } } diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java index 17bce30..39c12f0 100644 --- a/core/java/android/os/PowerManagerInternal.java +++ b/core/java/android/os/PowerManagerInternal.java @@ -117,6 +117,12 @@ public abstract class PowerManagerInternal { public abstract void setUserActivityTimeoutOverrideFromWindowManager(long timeoutMillis); /** + * Used by the window manager to tell the power manager that the user is no longer actively + * using the device. + */ + public abstract void setUserInactiveOverrideFromWindowManager(); + + /** * Used by device administration to set the maximum screen off timeout. * * This method must only be called by the device administration policy manager. @@ -153,4 +159,11 @@ public abstract class PowerManagerInternal { public abstract void uidGone(int uid); public abstract void powerHint(int hintId, int data); + + public abstract boolean setPowerSaveMode(boolean mode); + + public abstract void setFeature(int featureId, int data); + + public abstract int getFeature(int featureId); + } diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 7234e98..65b09eb 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -487,11 +487,12 @@ public class Process { String abi, String instructionSet, String appDataDir, + boolean refreshTheme, String[] zygoteArgs) { try { return startViaZygote(processClass, niceName, uid, gid, gids, debugFlags, mountExternal, targetSdkVersion, seInfo, - abi, instructionSet, appDataDir, zygoteArgs); + abi, instructionSet, appDataDir, refreshTheme, zygoteArgs); } catch (ZygoteStartFailedEx ex) { Log.e(LOG_TAG, "Starting VM process through Zygote failed"); @@ -610,6 +611,7 @@ public class Process { String abi, String instructionSet, String appDataDir, + boolean refreshTheme, String[] extraArgs) throws ZygoteStartFailedEx { synchronized(Process.class) { @@ -648,6 +650,9 @@ public class Process { } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) { argsForZygote.add("--mount-external-write"); } + if (refreshTheme) { + argsForZygote.add("--refresh_theme"); + } argsForZygote.add("--target-sdk-version=" + targetSdkVersion); //TODO optionally enable debuger diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index 0c79094..d277e65 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -335,18 +335,27 @@ public class RecoverySystem { throws IOException { String filename = packageFile.getCanonicalPath(); - FileWriter uncryptFile = new FileWriter(UNCRYPT_FILE); - try { - uncryptFile.write(filename + "\n"); - } finally { - uncryptFile.close(); - } - Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!"); + final String cryptoStatus = SystemProperties.get("ro.crypto.state", "unsupported"); + final boolean isEncrypted = "encrypted".equalsIgnoreCase(cryptoStatus); + + if (isEncrypted) { + FileWriter uncryptFile = new FileWriter(UNCRYPT_FILE); + try { + uncryptFile.write(filename + "\n"); + } finally { + uncryptFile.close(); + } + // UNCRYPT_FILE needs to be readable by system server on bootup. + if (!UNCRYPT_FILE.setReadable(true, false)) { + Log.e(TAG, "Error setting readable for " + UNCRYPT_FILE.getCanonicalPath()); + } + Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!"); - // If the package is on the /data partition, write the block map file - // into COMMAND_FILE instead. - if (filename.startsWith("/data/")) { - filename = "@/cache/recovery/block.map"; + // If the package is on the /data partition, write the block map file + // into COMMAND_FILE instead. + if (filename.startsWith("/data/")) { + filename = "@/cache/recovery/block.map"; + } } final String filenameArg = "--update_package=" + filename; @@ -368,21 +377,21 @@ public class RecoverySystem { * @throws SecurityException if the current user is not allowed to wipe data. */ public static void rebootWipeUserData(Context context) throws IOException { - rebootWipeUserData(context, false, context.getPackageName()); + rebootWipeUserData(context, false, context.getPackageName(), true); } /** {@hide} */ public static void rebootWipeUserData(Context context, String reason) throws IOException { - rebootWipeUserData(context, false, reason); + rebootWipeUserData(context, false, reason, true); } /** {@hide} */ public static void rebootWipeUserData(Context context, boolean shutdown) throws IOException { - rebootWipeUserData(context, shutdown, context.getPackageName()); + rebootWipeUserData(context, shutdown, context.getPackageName(), true); } - /** + /** * Reboots the device and wipes the user data and cache * partitions. This is sometimes called a "factory reset", which * is something of a misnomer because the system partition is not @@ -400,8 +409,8 @@ public class RecoverySystem { * * @hide */ - public static void rebootWipeUserData(Context context, boolean shutdown, String reason) - throws IOException { + public static void rebootWipeUserData(Context context, boolean shutdown, String reason, + boolean wipeMedia) throws IOException { UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); if (um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) { throw new SecurityException("Wiping data is not allowed for this user."); @@ -433,7 +442,13 @@ public class RecoverySystem { } final String localeArg = "--locale=" + Locale.getDefault().toString(); - bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg); + + String cmd = "--wipe_data\n"; + if (wipeMedia) { + cmd += "--wipe_media\n"; + } + + bootCommand(context, shutdownArg, cmd, reasonArg, localeArg); } /** @@ -501,6 +516,25 @@ public class RecoverySystem { Log.e(TAG, "Error reading recovery log", e); } + if (UNCRYPT_FILE.exists()) { + String filename = null; + try { + filename = FileUtils.readTextFile(UNCRYPT_FILE, 0, null); + } catch (IOException e) { + Log.e(TAG, "Error reading uncrypt file", e); + } + + // Remove the OTA package on /data that has been (possibly + // partially) processed. (Bug: 24973532) + if (filename != null && filename.startsWith("/data")) { + if (UNCRYPT_FILE.delete()) { + Log.i(TAG, "Deleted: " + filename); + } else { + Log.e(TAG, "Can't delete: " + filename); + } + } + } + // Delete everything in RECOVERY_DIR except those beginning // with LAST_PREFIX String[] names = RECOVERY_DIR.list(); diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index f10b982..f76a7c2 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -1928,9 +1928,9 @@ public final class StrictMode { // so we'll report it and bail on all of the current strict mode violations // we currently are maintaining for this thread. // First, drain the remaining violations from the parcel. - while (i < numViolations) { + i++; // Skip the current entry. + for (; i < numViolations; i++) { info = new ViolationInfo(p, !currentlyGathering); - i++; } // Next clear out all gathered violations. clearGatheredViolations(); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 045c1e8..5b532a3 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -489,6 +489,18 @@ public class UserManager { */ public static final String KEY_RESTRICTIONS_PENDING = "restrictions_pending"; + /** + * Specifies if the user is not allowed to use SU commands. + * The default value is <code>false</code>. + * + * <p/>Key for user restrictions. + * <p/>Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + * @hide + */ + public static final String DISALLOW_SU = "no_su"; + /** @hide */ public static final int PIN_VERIFICATION_FAILED_INCORRECT = -3; /** @hide */ @@ -853,6 +865,7 @@ public class UserManager { Bundle guestRestrictions = mService.getDefaultGuestRestrictions(); guestRestrictions.putBoolean(DISALLOW_SMS, true); guestRestrictions.putBoolean(DISALLOW_INSTALL_UNKNOWN_SOURCES, true); + guestRestrictions.putBoolean(DISALLOW_SU, true); mService.setUserRestrictions(guestRestrictions, guest.id); } catch (RemoteException re) { Log.w(TAG, "Could not update guest restrictions"); @@ -892,6 +905,7 @@ public class UserManager { private static void addDefaultUserRestrictions(Bundle restrictions) { restrictions.putBoolean(DISALLOW_OUTGOING_CALLS, true); restrictions.putBoolean(DISALLOW_SMS, true); + restrictions.putBoolean(DISALLOW_SU, true); } /** diff --git a/core/java/android/os/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java index 9114107..db0b0fd 100644 --- a/core/java/android/os/storage/DiskInfo.java +++ b/core/java/android/os/storage/DiskInfo.java @@ -47,6 +47,8 @@ public class DiskInfo implements Parcelable { public static final int FLAG_DEFAULT_PRIMARY = 1 << 1; public static final int FLAG_SD = 1 << 2; public static final int FLAG_USB = 1 << 3; + public static final int FLAG_EMMC = 1 << 4; + public static final int FLAG_NON_REMOVABLE = 1 << 5; public final String id; public final int flags; @@ -128,6 +130,10 @@ public class DiskInfo implements Parcelable { return (flags & FLAG_USB) != 0; } + public boolean isNonRemovable() { + return (flags & FLAG_NON_REMOVABLE) != 0; + } + @Override public String toString() { final CharArrayWriter writer = new CharArrayWriter(); diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java index 9f71ce1..5f84351 100644 --- a/core/java/android/os/storage/IMountService.java +++ b/core/java/android/os/storage/IMountService.java @@ -645,6 +645,24 @@ public interface IMountService extends IInterface { return _result; } + public int encryptWipeStorage(int type, String password) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + int _result; + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeInt(type); + _data.writeString(password); + mRemote.transact(Stub.TRANSACTION_encryptWipeStorage, _data, _reply, 0); + _reply.readException(); + _result = _reply.readInt(); + } finally { + _reply.recycle(); + _data.recycle(); + } + return _result; + } + public int changeEncryptionPassword(int type, String password) throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); @@ -1326,6 +1344,8 @@ public interface IMountService extends IInterface { static final int TRANSACTION_benchmark = IBinder.FIRST_CALL_TRANSACTION + 59; static final int TRANSACTION_setDebugFlags = IBinder.FIRST_CALL_TRANSACTION + 60; + static final int TRANSACTION_encryptWipeStorage = IBinder.FIRST_CALL_TRANSACTION + 61; + static final int TRANSACTION_createNewUserDir = IBinder.FIRST_CALL_TRANSACTION + 62; static final int TRANSACTION_deleteUserKey = IBinder.FIRST_CALL_TRANSACTION + 63; @@ -1631,6 +1651,15 @@ public interface IMountService extends IInterface { reply.writeInt(result); return true; } + case TRANSACTION_encryptWipeStorage: { + data.enforceInterface(DESCRIPTOR); + int type = data.readInt(); + String password = data.readString(); + int result = encryptWipeStorage(type, password); + reply.writeNoException(); + reply.writeInt(result); + return true; + } case TRANSACTION_changeEncryptionPassword: { data.enforceInterface(DESCRIPTOR); int type = data.readInt(); @@ -2065,7 +2094,8 @@ public interface IMountService extends IInterface { * Returns whether or not the external storage is emulated. */ public boolean isExternalStorageEmulated() throws RemoteException; - + /** The volume has been encrypted succesfully and MDTP state is 'activated'. */ + static final int ENCRYPTION_STATE_OK_MDTP_ACTIVATED = 2; /** The volume is not encrypted. */ static final int ENCRYPTION_STATE_NONE = 1; /** The volume has been encrypted succesfully. */ @@ -2078,6 +2108,8 @@ public interface IMountService extends IInterface { static final int ENCRYPTION_STATE_ERROR_INCONSISTENT = -3; /** Underlying data is corrupt */ static final int ENCRYPTION_STATE_ERROR_CORRUPT = -4; + /** The volume is in a bad state and MDTP state is 'activated'.*/ + static final int ENCRYPTION_STATE_ERROR_MDTP_ACTIVATED = -5; /** * Determines the encryption state of the volume. @@ -2096,6 +2128,11 @@ public interface IMountService extends IInterface { public int encryptStorage(int type, String password) throws RemoteException; /** + * Encrypts and wipes storage. + */ + public int encryptWipeStorage(int type, String password) throws RemoteException; + + /** * Changes the encryption password. */ public int changeEncryptionPassword(int type, String password) diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java index 1408202..4dbd0a6 100644 --- a/core/java/android/os/storage/StorageVolume.java +++ b/core/java/android/os/storage/StorageVolume.java @@ -196,15 +196,20 @@ public class StorageVolume implements Parcelable { } /** - * Parse and return volume UUID as FAT volume ID, or return -1 if unable to + * Parse and return volume UUID as volume ID, or return -1 if unable to * parse or UUID is unknown. */ - public int getFatVolumeId() { - if (mFsUuid == null || mFsUuid.length() != 9) { + public int getVolumeId() { + String id = mFsUuid; + if (id == null) { return -1; } + id = id.replace("-", ""); + if (id.length() > 8) { + id = id.substring(0, 8); + } try { - return (int) Long.parseLong(mFsUuid.replace("-", ""), 16); + return (int) Long.parseLong(id, 16); } catch (NumberFormatException e) { return -1; } diff --git a/core/java/android/preference/DialogPreference.java b/core/java/android/preference/DialogPreference.java index 3d57b4d..1aec3ec 100644 --- a/core/java/android/preference/DialogPreference.java +++ b/core/java/android/preference/DialogPreference.java @@ -285,6 +285,22 @@ public abstract class DialogPreference extends Preference implements * @param state Optional instance state to restore on the dialog */ protected void showDialog(Bundle state) { + // Create the dialog + final Dialog dialog = mDialog = createDialog(); + if (state != null) { + dialog.onRestoreInstanceState(state); + } + if (needInputMethod()) { + requestInputMethod(dialog); + } + dialog.setOnDismissListener(this); + dialog.show(); + } + + /** + * @hide + */ + protected Dialog createDialog() { Context context = getContext(); mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE; @@ -306,17 +322,8 @@ public abstract class DialogPreference extends Preference implements onPrepareDialogBuilder(mBuilder); getPreferenceManager().registerOnActivityDestroyListener(this); - - // Create the dialog - final Dialog dialog = mDialog = mBuilder.create(); - if (state != null) { - dialog.onRestoreInstanceState(state); - } - if (needInputMethod()) { - requestInputMethod(dialog); - } - dialog.setOnDismissListener(this); - dialog.show(); + + return mBuilder.create(); } /** diff --git a/core/java/android/preference/MultiSelectListPreference.java b/core/java/android/preference/MultiSelectListPreference.java index 138bd87..6acfd8d 100644..100755 --- a/core/java/android/preference/MultiSelectListPreference.java +++ b/core/java/android/preference/MultiSelectListPreference.java @@ -220,7 +220,7 @@ public class MultiSelectListPreference extends DialogPreference { @Override protected Object onGetDefaultValue(TypedArray a, int index) { final CharSequence[] defaultValues = a.getTextArray(index); - final int valueCount = defaultValues.length; + final int valueCount = defaultValues != null ? defaultValues.length : 0; final Set<String> result = new HashSet<String>(); for (int i = 0; i < valueCount; i++) { @@ -247,7 +247,20 @@ public class MultiSelectListPreference extends DialogPreference { myState.values = getValues(); return myState; } - + + @Override + protected void onRestoreInstanceState(Parcelable state) { + if (!state.getClass().equals(SavedState.class)) { + // Didn't save state for us in onSaveInstanceState + super.onRestoreInstanceState(state); + return; + } + + SavedState myState = (SavedState) state; + super.onRestoreInstanceState(myState.getSuperState()); + setValues(myState.values); + } + private static class SavedState extends BaseSavedState { Set<String> values; diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java index 3b482eb..b15a7e5 100644 --- a/core/java/android/preference/Preference.java +++ b/core/java/android/preference/Preference.java @@ -1377,10 +1377,18 @@ public class Preference implements Comparable<Preference> { mDefaultValue = defaultValue; } + /** + * Returns whether the preference can be found in persistent storage + * @hide + */ + protected boolean isPersisted() { + return getSharedPreferences().contains(mKey); + } + private void dispatchSetInitialValue() { // By now, we know if we are persistent. final boolean shouldPersist = shouldPersist(); - if (!shouldPersist || !getSharedPreferences().contains(mKey)) { + if (!shouldPersist || !isPersisted()) { if (mDefaultValue != null) { onSetInitialValue(false, mDefaultValue); } diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java index 66642de..db04c71 100644 --- a/core/java/android/preference/PreferenceFragment.java +++ b/core/java/android/preference/PreferenceFragment.java @@ -214,6 +214,9 @@ public abstract class PreferenceFragment extends Fragment implements @Override public void onDestroyView() { + if (mList != null) { + mList.setOnKeyListener(null); + } mList = null; mHandler.removeCallbacks(mRequestFocus); mHandler.removeMessages(MSG_BIND_PREFERENCES); diff --git a/core/java/android/preference/PreferenceGroup.java b/core/java/android/preference/PreferenceGroup.java index 5e84086..f17506b 100644 --- a/core/java/android/preference/PreferenceGroup.java +++ b/core/java/android/preference/PreferenceGroup.java @@ -148,16 +148,15 @@ public abstract class PreferenceGroup extends Preference implements GenericInfla } } - int insertionIndex = Collections.binarySearch(mPreferenceList, preference); - if (insertionIndex < 0) { - insertionIndex = insertionIndex * -1 - 1; - } - if (!onPrepareAddPreference(preference)) { return false; } synchronized(this) { + int insertionIndex = Collections.binarySearch(mPreferenceList, preference); + if (insertionIndex < 0) { + insertionIndex = insertionIndex * -1 - 1; + } mPreferenceList.add(insertionIndex, preference); } diff --git a/core/java/android/preference/RingtonePreference.java b/core/java/android/preference/RingtonePreference.java index a76bb09..c730329 100644 --- a/core/java/android/preference/RingtonePreference.java +++ b/core/java/android/preference/RingtonePreference.java @@ -48,8 +48,10 @@ public class RingtonePreference extends Preference implements private int mRingtoneType; private boolean mShowDefault; private boolean mShowSilent; + private int mDialogStyle; private int mRequestCode; + private int mSubscriptionID = 0; /* Sub-1 by default */ public RingtonePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); @@ -62,6 +64,8 @@ public class RingtonePreference extends Preference implements true); mShowSilent = a.getBoolean(com.android.internal.R.styleable.RingtonePreference_showSilent, true); + mDialogStyle = a.getResourceId( + com.android.internal.R.styleable.RingtonePreference_dialogStyle, 0); a.recycle(); } @@ -98,6 +102,28 @@ public class RingtonePreference extends Preference implements } /** + * Returns the subscription ID. + * + * @return The current subscription ID. + * @see #setSubId(int) + * @hide + */ + public int getSubId() { + return mSubscriptionID; + } + + /** + * Sets the subscription ID. + * + * @param subId subscription ID. + * @see #getSubId(int) + * @hide + */ + public void setSubId(int subId) { + mSubscriptionID = subId; + } + + /** * Returns whether to a show an item for the default sound/ringtone. * * @return Whether to show an item for the default sound/ringtone. @@ -136,6 +162,27 @@ public class RingtonePreference extends Preference implements mShowSilent = showSilent; } + /** + * Returns the resource id style of the ringtone dialog. + * + * @return The resource id of the style + * @hide + */ + public int getDialogStyle() { + return mDialogStyle; + } + + /** + * Sets the resource id style of the ringtone dialog. + * + * @param dialogStyle The resource id of the style. + * @see RingtoneManager#EXTRA_RINGTONE_DIALOG_THEME + * @hide + */ + public void setDialogStyle(int dialogStyle) { + mDialogStyle = dialogStyle; + } + @Override protected void onClick() { // Launch the ringtone picker @@ -163,8 +210,17 @@ public class RingtonePreference extends Preference implements ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, mShowDefault); if (mShowDefault) { - ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, + if (getRingtoneType() == RingtoneManager.TYPE_RINGTONE) { + ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, + RingtoneManager.getActualRingtoneUriBySubId(getContext(), getSubId())); + } else { + ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, RingtoneManager.getDefaultUri(getRingtoneType())); + } + } + if (mDialogStyle != 0) { + ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_DIALOG_THEME, + mDialogStyle); } ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, mShowSilent); diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java index 2445bc2..e5f71a0 100644 --- a/core/java/android/preference/SeekBarVolumizer.java +++ b/core/java/android/preference/SeekBarVolumizer.java @@ -59,6 +59,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba private final NotificationManager mNotificationManager; private final int mStreamType; private final int mMaxStreamVolume; + private final boolean mVoiceCapable; private boolean mAffectedByRingerMode; private boolean mNotificationOrRing; private final Receiver mReceiver = new Receiver(); @@ -110,12 +111,19 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba } } mDefaultUri = defaultUri; + mVoiceCapable = context.getResources().getBoolean( + com.android.internal.R.bool.config_voice_capable); } private static boolean isNotificationOrRing(int stream) { return stream == AudioManager.STREAM_RING || stream == AudioManager.STREAM_NOTIFICATION; } + private boolean isNotificationStreamLinked() { + return mVoiceCapable && Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.VOLUME_LINK_NOTIFICATION, 1) == 1; + } + public void setSeekBar(SeekBar seekBar) { if (mSeekBar != null) { mSeekBar.setOnSeekBarChangeListener(null); @@ -139,13 +147,19 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba mSeekBar.setProgress(mLastAudibleStreamVolume); } else if (mNotificationOrRing && mRingerMode == AudioManager.RINGER_MODE_VIBRATE) { mSeekBar.setProgress(0); + mSeekBar.setEnabled(mStreamType == AudioManager.STREAM_RING); } else if (mMuted) { mSeekBar.setProgress(0); } else { + mSeekBar.setEnabled(enableSeekBar()); mSeekBar.setProgress(mLastProgress > -1 ? mLastProgress : mOriginalStreamVolume); } } + private boolean enableSeekBar() { + return !(mStreamType == AudioManager.STREAM_NOTIFICATION && isNotificationStreamLinked()); + } + @Override public boolean handleMessage(Message msg) { switch (msg.what) { @@ -250,7 +264,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba } public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) { - if (fromTouch) { + if (fromTouch && enableSeekBar()) { postSetVolume(progress); } if (mCallback != null) { @@ -398,10 +412,11 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba int streamValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1); updateVolumeSlider(streamType, streamValue); } else if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) { + final int oldRingerMode = mRingerMode; if (mNotificationOrRing) { mRingerMode = mAudioManager.getRingerModeInternal(); } - if (mAffectedByRingerMode) { + if (mAffectedByRingerMode && oldRingerMode != mRingerMode) { updateSlider(); } } else if (AudioManager.STREAM_DEVICES_CHANGED_ACTION.equals(action)) { @@ -415,7 +430,8 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba } private void updateVolumeSlider(int streamType, int streamValue) { - final boolean streamMatch = mNotificationOrRing ? isNotificationOrRing(streamType) + final boolean streamMatch = mNotificationOrRing && isNotificationStreamLinked() + ? isNotificationOrRing(streamType) : (streamType == mStreamType); if (mSeekBar != null && streamMatch && streamValue != -1) { final boolean muted = mAudioManager.isStreamMute(mStreamType) diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java index 7d05522..299a95c 100644 --- a/core/java/android/provider/Browser.java +++ b/core/java/android/provider/Browser.java @@ -244,6 +244,7 @@ public class Browser { */ public static final Cursor getAllBookmarks(ContentResolver cr) throws IllegalStateException { + android.util.SeempLog.record(32); return new MatrixCursor(new String[]{Bookmarks.URL}, 0); } @@ -256,6 +257,7 @@ public class Browser { */ public static final Cursor getAllVisitedUrls(ContentResolver cr) throws IllegalStateException { + android.util.SeempLog.record(33); return new MatrixCursor(new String[]{Combined.URL}, 0); } @@ -264,6 +266,7 @@ public class Browser { } private static final Cursor getVisitedLike(ContentResolver cr, String url) { + android.util.SeempLog.record(34); boolean secure = false; String compareString = url; if (compareString.startsWith("http://")) { @@ -324,6 +327,7 @@ public class Browser { */ @Deprecated public static final String[] getVisitedHistory(ContentResolver cr) { + android.util.SeempLog.record(35); return new String[0]; } @@ -359,6 +363,7 @@ public class Browser { * @removed */ public static final void clearHistory(ContentResolver cr) { + android.util.SeempLog.record(37); } @@ -420,6 +425,7 @@ public class Browser { */ public static final void requestAllIcons(ContentResolver cr, String where, WebIconDatabase.IconListener listener) { + android.util.SeempLog.record(36); // Do nothing: this is no longer used. } diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java index aa22041..2ede026 100644 --- a/core/java/android/provider/CalendarContract.java +++ b/core/java/android/provider/CalendarContract.java @@ -871,6 +871,7 @@ public final class CalendarContract { * @return A Cursor containing all attendees for the event */ public static final Cursor query(ContentResolver cr, long eventId, String[] projection) { + android.util.SeempLog.record(54); String[] attArgs = {Long.toString(eventId)}; return cr.query(CONTENT_URI, projection, ATTENDEES_WHERE, attArgs /* selection args */, null /* sort order */); @@ -1750,6 +1751,7 @@ public final class CalendarContract { */ public static final Cursor query(ContentResolver cr, String[] projection, long begin, long end) { + android.util.SeempLog.record(54); Uri.Builder builder = CONTENT_URI.buildUpon(); ContentUris.appendId(builder, begin); ContentUris.appendId(builder, end); @@ -1779,6 +1781,7 @@ public final class CalendarContract { */ public static final Cursor query(ContentResolver cr, String[] projection, long begin, long end, String searchQuery) { + android.util.SeempLog.record(54); Uri.Builder builder = CONTENT_SEARCH_URI.buildUpon(); ContentUris.appendId(builder, begin); ContentUris.appendId(builder, end); @@ -2029,6 +2032,7 @@ public final class CalendarContract { */ public static final Cursor query(ContentResolver cr, int startDay, int numDays, String[] projection) { + android.util.SeempLog.record(54); if (numDays < 1) { return null; } @@ -2112,6 +2116,7 @@ public final class CalendarContract { * @return A Cursor containing all reminders for the event */ public static final Cursor query(ContentResolver cr, long eventId, String[] projection) { + android.util.SeempLog.record(54); String[] remArgs = {Long.toString(eventId)}; return cr.query(CONTENT_URI, projection, REMINDERS_WHERE, remArgs /*selection args*/, null /* sort order */); @@ -2262,6 +2267,7 @@ public final class CalendarContract { */ public static final Uri insert(ContentResolver cr, long eventId, long begin, long end, long alarmTime, int minutes) { + android.util.SeempLog.record(51); ContentValues values = new ContentValues(); values.put(CalendarAlerts.EVENT_ID, eventId); values.put(CalendarAlerts.BEGIN, begin); @@ -2289,6 +2295,7 @@ public final class CalendarContract { * @hide */ public static final long findNextAlarmTime(ContentResolver cr, long millis) { + android.util.SeempLog.record(53); String selection = ALARM_TIME + ">=" + millis; // TODO: construct an explicit SQL query so that we can add // "LIMIT 1" to the end and get just one result. @@ -2412,6 +2419,7 @@ public final class CalendarContract { */ public static final boolean alarmExists(ContentResolver cr, long eventId, long begin, long alarmTime) { + android.util.SeempLog.record(52); // TODO: construct an explicit SQL query so that we can add // "LIMIT 1" to the end and get just one result. String[] projection = new String[] { ALARM_TIME }; diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index 4f880b1..f6c68dd 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -27,6 +27,7 @@ import android.database.Cursor; import android.location.Country; import android.location.CountryDetector; import android.net.Uri; +import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; import android.provider.ContactsContract.CommonDataKinds.Callable; @@ -164,6 +165,10 @@ public class CallLog { public static final int MISSED_TYPE = 3; /** Call log type for voicemails. */ public static final int VOICEMAIL_TYPE = 4; + /** Call log type for blacklisted calls + * @hide + */ + public static final int BLACKLIST_TYPE = 5; /** * Bit-mask describing features of the call (e.g. video). @@ -397,6 +402,12 @@ public class CallLog { private static final int MIN_DURATION_FOR_NORMALIZED_NUMBER_UPDATE_MS = 1000 * 10; /** + * If a call has an origin inside of the OS, this column will be filled out. + * <P>Type: String </P> + */ + private static final String ORIGIN = "origin"; + + /** * Adds a call to the call log. * * @param ci the CallerInfo object to get the target contact from. Can be null @@ -413,15 +424,16 @@ public class CallLog { * @param duration call duration in seconds * @param dataUsage data usage for the call in bytes, null if data usage was not tracked for * the call. + * @param callExtras Bundle of extra data from the call. * @result The URI of the call log entry belonging to the user that made or received this * call. * {@hide} */ public static Uri addCall(CallerInfo ci, Context context, String number, int presentation, int callType, int features, PhoneAccountHandle accountHandle, - long start, int duration, Long dataUsage) { + long start, int duration, Long dataUsage, Bundle callExtras) { return addCall(ci, context, number, presentation, callType, features, accountHandle, - start, duration, dataUsage, false, false); + start, duration, dataUsage, false, false, callExtras); } @@ -444,16 +456,17 @@ public class CallLog { * the call. * @param addForAllUsers If true, the call is added to the call log of all currently * running users. The caller must have the MANAGE_USERS permission if this is true. - * + * @param callExtras Bundle of extra data from the call. * @result The URI of the call log entry belonging to the user that made or received this * call. * {@hide} */ public static Uri addCall(CallerInfo ci, Context context, String number, int presentation, int callType, int features, PhoneAccountHandle accountHandle, - long start, int duration, Long dataUsage, boolean addForAllUsers) { + long start, int duration, Long dataUsage, boolean addForAllUsers, + Bundle callExtras) { return addCall(ci, context, number, presentation, callType, features, accountHandle, - start, duration, dataUsage, addForAllUsers, false); + start, duration, dataUsage, addForAllUsers, false, callExtras); } /** @@ -477,6 +490,7 @@ public class CallLog { * running users. The caller must have the MANAGE_USERS permission if this is true. * @param is_read Flag to show if the missed call log has been read by the user or not. * Used for call log restore of missed calls. + * @param callExtras Bundle of extra data from the call. * * @result The URI of the call log entry belonging to the user that made or received this * call. @@ -484,7 +498,8 @@ public class CallLog { */ public static Uri addCall(CallerInfo ci, Context context, String number, int presentation, int callType, int features, PhoneAccountHandle accountHandle, - long start, int duration, Long dataUsage, boolean addForAllUsers, boolean is_read) { + long start, int duration, Long dataUsage, boolean addForAllUsers, boolean is_read, + Bundle callExtras) { final ContentResolver resolver = context.getContentResolver(); int numberPresentation = PRESENTATION_ALLOWED; @@ -543,6 +558,9 @@ public class CallLog { if (dataUsage != null) { values.put(DATA_USAGE, dataUsage); } + if (callExtras != null && callExtras.containsKey(PhoneConstants.EXTRA_CALL_ORIGIN)) { + values.put(ORIGIN, callExtras.getString(PhoneConstants.EXTRA_CALL_ORIGIN)); + } values.put(PHONE_ACCOUNT_COMPONENT_NAME, accountComponentString); values.put(PHONE_ACCOUNT_ID, accountId); values.put(PHONE_ACCOUNT_ADDRESS, accountAddress); diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 8ce1cbf..0e7f487 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -1119,7 +1119,15 @@ public final class ContactsContract { public static final String SORT_KEY_ALTERNATIVE = "sort_key_alt"; } - interface ContactCounts { + /** + * URI parameter and cursor extras that return counts of rows grouped by the + * address book index, which is usually the first letter of the sort key. + * When this parameter is supplied, the row counts are returned in the + * cursor extras bundle. + * + * @hide + */ + public interface ContactCounts { /** * Add this query parameter to a URI to get back row counts grouped by the address book @@ -1497,6 +1505,7 @@ public final class ContactsContract { * {@link #CONTENT_LOOKUP_URI} to attempt refreshing. */ public static Uri getLookupUri(ContentResolver resolver, Uri contactUri) { + android.util.SeempLog.record(86); final Cursor c = resolver.query(contactUri, new String[] { Contacts.LOOKUP_KEY, Contacts._ID }, null, null, null); @@ -1524,6 +1533,7 @@ public final class ContactsContract { * provided parameters. */ public static Uri getLookupUri(long contactId, String lookupKey) { + android.util.SeempLog.record(86); if (TextUtils.isEmpty(lookupKey)) { return null; } @@ -1537,6 +1547,7 @@ public final class ContactsContract { * Returns null if the contact cannot be found. */ public static Uri lookupContact(ContentResolver resolver, Uri lookupUri) { + android.util.SeempLog.record(87); if (lookupUri == null) { return null; } @@ -1999,6 +2010,7 @@ public final class ContactsContract { */ public static InputStream openContactPhotoInputStream(ContentResolver cr, Uri contactUri, boolean preferHighres) { + android.util.SeempLog.record(88); if (preferHighres) { final Uri displayPhotoUri = Uri.withAppendedPath(contactUri, Contacts.Photo.DISPLAY_PHOTO); @@ -2046,6 +2058,7 @@ public final class ContactsContract { * of the thumbnail the high-res picture is preferred */ public static InputStream openContactPhotoInputStream(ContentResolver cr, Uri contactUri) { + android.util.SeempLog.record(88); return openContactPhotoInputStream(cr, contactUri, false); } } @@ -2738,6 +2751,7 @@ public final class ContactsContract { * entry of the given {@link RawContacts} entry. */ public static Uri getContactLookupUri(ContentResolver resolver, Uri rawContactUri) { + android.util.SeempLog.record(89); // TODO: use a lighter query by joining rawcontacts with contacts in provider final Uri dataUri = Uri.withAppendedPath(rawContactUri, Data.CONTENT_DIRECTORY); final Cursor cursor = resolver.query(dataUri, new String[] { @@ -4684,6 +4698,7 @@ public final class ContactsContract { * </p> */ public static Uri getContactLookupUri(ContentResolver resolver, Uri dataUri) { + android.util.SeempLog.record(89); final Cursor cursor = resolver.query(dataUri, new String[] { RawContacts.CONTACT_ID, Contacts.LOOKUP_KEY }, null, null, null); diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java index b2d9b93..961eb19 100644 --- a/core/java/android/provider/Downloads.java +++ b/core/java/android/provider/Downloads.java @@ -593,6 +593,11 @@ public final class Downloads { public static final int STATUS_QUEUED_FOR_WIFI = 196; /** + * This download is paused by manual. + */ + public static final int STATUS_PAUSED_BY_MANUAL = 197; + + /** * This download couldn't be completed due to insufficient storage * space. Typically, this is because the SD card is full. */ diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 14f8fdd..3ab16fe 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -64,11 +64,14 @@ import com.android.internal.widget.ILockSettings; import java.net.URISyntaxException; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.regex.Pattern; /** * The Settings provider contains global system-level device preferences. @@ -657,6 +660,19 @@ public final class Settings { /** * @hide + * Activity Action: Show the "app ops" details screen. + * <p> + * Input: The Intent's data URI specifies the application package name + * to be shown, with the "package" scheme. That is "package:com.my.app". + * <p> + * Output: Nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_APP_OPS_DETAILS_SETTINGS = + "android.settings.APP_OPS_DETAILS_SETTINGS"; + + /** + * @hide * Activity Action: Show the "app ops" settings screen. * <p> * Input: Nothing. @@ -1492,6 +1508,11 @@ public final class Settings { // At one time in System, then Global, but now back in Secure MOVED_TO_SECURE.add(Secure.INSTALL_NON_MARKET_APPS); + + MOVED_TO_SECURE.add(System.KEYBOARD_BRIGHTNESS); + MOVED_TO_SECURE.add(System.BUTTON_BRIGHTNESS); + MOVED_TO_SECURE.add(System.BUTTON_BACKLIGHT_TIMEOUT); + MOVED_TO_SECURE.add(Secure.VOLUME_LINK_NOTIFICATION); } private static final HashSet<String> MOVED_TO_GLOBAL; @@ -1583,6 +1604,44 @@ public final class Settings { } }; + /** + * Put a delimited list as a string + * @param resolver to access the database with + * @param name to store + * @param delimiter to split + * @param list to join and store + * @hide + */ + public static void putListAsDelimitedString(ContentResolver resolver, String name, + String delimiter, List<String> list) { + String store = TextUtils.join(delimiter, list); + putString(resolver, name, store); + } + + /** + * Get a delimited string returned as a list + * @param resolver to access the database with + * @param name to store + * @param delimiter to split the list with + * @return list of strings for a specific Settings.Secure item + * @hide + */ + public static List<String> getDelimitedStringAsList(ContentResolver resolver, String name, + String delimiter) { + String baseString = getString(resolver, name); + List<String> list = new ArrayList<String>(); + if (!TextUtils.isEmpty(baseString)) { + final String[] array = TextUtils.split(baseString, Pattern.quote(delimiter)); + for (String item : array) { + if (TextUtils.isEmpty(item)) { + continue; + } + list.add(item); + } + } + return list; + } + /** @hide */ public static void getMovedToGlobalSettings(Set<String> outKeySet) { outKeySet.addAll(MOVED_TO_GLOBAL); @@ -1612,6 +1671,7 @@ public final class Settings { /** @hide */ public static String getStringForUser(ContentResolver resolver, String name, int userHandle) { + android.util.SeempLog.record(android.util.SeempLog.getSeempGetApiIdFromValue(name)); if (MOVED_TO_SECURE.contains(name)) { Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System" + " to android.provider.Settings.Secure, returning read-only value."); @@ -1639,6 +1699,7 @@ public final class Settings { /** @hide */ public static boolean putStringForUser(ContentResolver resolver, String name, String value, int userHandle) { + android.util.SeempLog.record(android.util.SeempLog.getSeempPutApiIdFromValue(name)); if (MOVED_TO_SECURE.contains(name)) { Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System" + " to android.provider.Settings.Secure, value is unchanged."); @@ -2433,6 +2494,31 @@ public final class Settings { public static final int SCREEN_BRIGHTNESS_MODE_AUTOMATIC = 1; /** + * The keyboard brightness to be used while the screen is on. + * Valid value range is between 0 and {@link PowerManager#getMaximumKeyboardBrightness()} + * @deprecated + * @hide + */ + public static final String KEYBOARD_BRIGHTNESS = "keyboard_brightness"; + + /** + * The button brightness to be used while the screen is on or after a button press, + * depending on the value of {@link BUTTON_BACKLIGHT_TIMEOUT}. + * Valid value range is between 0 and {@link PowerManager#getMaximumButtonBrightness()} + * @deprecated + * @hide + */ + public static final String BUTTON_BRIGHTNESS = "button_brightness"; + + /** + * The time in ms to keep the button backlight on after pressing a button. + * A value of 0 will keep the buttons on for as long as the screen is on. + * @deprecated + * @hide + */ + public static final String BUTTON_BACKLIGHT_TIMEOUT = "button_backlight_timeout"; + + /** * Control whether the process CPU usage meter should be shown. * * @deprecated Use {@link Global#SHOW_PROCESSES} instead @@ -2596,6 +2682,55 @@ public final class Settings { private static final Validator NOTIFICATIONS_USE_RING_VOLUME_VALIDATOR = sBooleanValidator; /** + * Whether the blacklisting feature for phone calls is enabled + * @hide + */ + public static final String PHONE_BLACKLIST_ENABLED = "phone_blacklist_enabled"; + + /** + * Whether a notification should be shown when a call/message is blocked + * @hide + */ + public static final String PHONE_BLACKLIST_NOTIFY_ENABLED = "phone_blacklist_notify_enabled"; + + /** + * Whether the blacklisting feature for phone calls from private numbers is enabled + * @hide + */ + public static final String PHONE_BLACKLIST_PRIVATE_NUMBER_MODE = "phone_blacklist_private_number_enabled"; + + /** + * Whether the blacklisting feature for phone calls from unknown numbers is enabled + * @hide + */ + public static final String PHONE_BLACKLIST_UNKNOWN_NUMBER_MODE = "phone_blacklist_unknown_number_enabled"; + + /** + * Constants to be used for {@link PHONE_BLACKLIST_PRIVATE_NUMBER_MODE} and + * {@link PHONE_BLACKLIST_UNKNOWN_NUMBER_MODE}. + * @hide + */ + public static final int BLACKLIST_DO_NOT_BLOCK = 0; + /** + * @hide + */ + public static final int BLACKLIST_BLOCK = 1; + /** + * @hide + */ + public static final int BLACKLIST_PHONE_SHIFT = 0; + /** + * @hide + */ + public static final int BLACKLIST_MESSAGE_SHIFT = 4; + + /** + * Whether the regex blacklisting feature for phone calls is enabled + * @hide + */ + public static final String PHONE_BLACKLIST_REGEX_ENABLED = "phone_blacklist_regex_enabled"; + + /** * Whether silent mode should allow vibration feedback. This is used * internally in AudioService and the Sound settings activity to * coordinate decoupling of vibrate and silent modes. This setting @@ -2644,6 +2779,30 @@ public final class Settings { private static final Validator RINGTONE_VALIDATOR = sUriValidator; /** + * Persistent store for the SIM-2 ringtone URI. + * <p> + * If you need to play SIM-2 ringtone at any given time, it is recommended + * you give {@link #DEFAULT_RINGTONE_URI_2} to the media player. It will resolve + * to the set default ringtone at the time of playing. + * + * @see #DEFAULT_RINGTONE_URI_2 + * @hide + */ + public static final String RINGTONE_2 = "ringtone_2"; + + /** + * Persistent store for the SIM-3 ringtone URI. + * <p> + * If you need to play SIM-3 ringtone at any given time, it is recommended + * you give {@link #DEFAULT_RINGTONE_URI_3} to the media player. It will resolve + * to the set default ringtone at the time of playing. + * + * @see #DEFAULT_RINGTONE_URI_3 + * @hide + */ + public static final String RINGTONE_3 = "ringtone_3"; + + /** * A {@link Uri} that will point to the current default ringtone at any * given time. * <p> @@ -2654,6 +2813,39 @@ public final class Settings { public static final Uri DEFAULT_RINGTONE_URI = getUriFor(RINGTONE); /** + * A {@link Uri} that will point to the current SIM-2 ringtone at any + * given time. + * <p> + * If the current default ringtone is in the DRM provider and the caller + * does not have permission, the exception will be a + * FileNotFoundException. + * + * @hide + */ + public static final Uri DEFAULT_RINGTONE_URI_2 = getUriFor(RINGTONE_2); + + /** + * A {@link Uri} that will point to the current SIM-3 ringtone at any + * given time. + * <p> + * If the current default ringtone is in the DRM provider and the caller + * does not have permission, the exception will be a + * FileNotFoundException. + * + * @hide + */ + public static final Uri DEFAULT_RINGTONE_URI_3 = getUriFor(RINGTONE_3); + + /** + * Maximum number of ringtones supported. + * <p> + * Maximum number of ringtones supported by settings. Increment this + * if a new URI needs to be added for ringtone. + * @hide + */ + public static final int MAX_NUM_RINGTONES = 3; + + /** * Persistent store for the system-wide default notification sound. * * @see #RINGTONE @@ -2723,6 +2915,15 @@ public final class Settings { private static final Validator TEXT_AUTO_CAPS_VALIDATOR = sBooleanValidator; /** + * Setting to show if system is in power off alarm mode. 1 = true, 0 = false + * @hide + */ + public static final String POWER_OFF_ALARM_MODE = "power_off_alarm_mode"; + + /** Validator for POWER_OFF_ALARM_MODE */ + private static final Validator POWER_OFF_ALARM_MODE_VALIDATOR = sBooleanValidator; + + /** * Setting to enable Auto Punctuate in text editors. 1 = On, 0 = Off. This * feature converts two spaces to a "." and space. */ @@ -2788,6 +2989,14 @@ public final class Settings { new DiscreteValueValidator(new String[] {"12", "24"}); /** + * Developer options - Navigation Bar show switch + * @deprecated Moved to CMSettings.Secure.DEV_FORCE_SHOW_NAVBAR + * @hide + */ + public static final String DEV_FORCE_SHOW_NAVBAR = "dev_force_show_navbar"; + + + /** * Date format string * mm/dd/yyyy * dd/mm/yyyy @@ -2849,6 +3058,19 @@ public final class Settings { public static final String ANIMATOR_DURATION_SCALE = Global.ANIMATOR_DURATION_SCALE; /** + * Control the type of rotation which can be performed using the accelerometer + * if ACCELEROMETER_ROTATION is enabled. + * Value is a bitwise combination of + * 1 = 0 degrees (portrait) + * 2 = 90 degrees (left) + * 4 = 180 degrees (inverted portrait) + * 8 = 270 degrees (right) + * Setting to 0 is effectively orientation lock + * @hide + */ + public static final String ACCELEROMETER_ROTATION_ANGLES = "accelerometer_rotation_angles"; + + /** * Control whether the accelerometer will be used to change screen * orientation. If 0, it will not be used unless explicitly requested * by the application; if 1, it will be used by default unless explicitly @@ -3183,6 +3405,63 @@ public final class Settings { new InclusiveFloatRangeValidator(-7, 7); /** + * Show icon when stylus is used? + * 0 = no + * 1 = yes + * @hide + */ + public static final String STYLUS_ICON_ENABLED = "stylus_icon_enabled"; + + /** + * Enable Stylus Gestures + * + * @hide + */ + public static final String ENABLE_STYLUS_GESTURES = "enable_stylus_gestures"; + + /** + * Left Swipe Action + * + * @hide + */ + public static final String GESTURES_LEFT_SWIPE = "gestures_left_swipe"; + + /** + * Right Swipe Action + * + * @hide + */ + public static final String GESTURES_RIGHT_SWIPE = "gestures_right_swipe"; + + /** + * Up Swipe Action + * + * @hide + */ + public static final String GESTURES_UP_SWIPE = "gestures_up_swipe"; + + /** + * down Swipe Action + * + * @hide + */ + public static final String GESTURES_DOWN_SWIPE = "gestures_down_swipe"; + + /** + * Long press Action + * + * @hide + */ + public static final String GESTURES_LONG_PRESS = "gestures_long_press"; + + /** + * double tap Action + * + * @hide + */ + public static final String GESTURES_DOUBLE_TAP = "gestures_double_tap"; + + /** * Whether lock-to-app will be triggered by long-press on recents. * @hide */ @@ -3219,6 +3498,44 @@ public final class Settings { * the setting value. See an example above. */ + /** + * Whether wifi settings will connect to access point automatically + * 0 = automatically + * 1 = manually + * @hide + */ + public static final String WIFI_AUTO_CONNECT_TYPE = "wifi_auto_connect_type"; + + /** + * Volume keys control cursor in text fields (default is 0) + * 0 - Disabled + * 1 - Volume up/down moves cursor left/right + * 2 - Volume up/down moves cursor right/left + * @hide + */ + public static final String VOLUME_KEY_CURSOR_CONTROL = "volume_key_cursor_control"; + + /** + * Whether to enable voice wakeup. The value is boolean (1 or 0). + * @hide + */ + public static final String VOICE_WAKEUP = "voice_wakeup"; + + /** + * An intent (a flattened Uri String) to launch when user voice launch + * action is detected. An empty or null string will launch the default + * voice search activity. + * @hide + */ + public static final String VOICE_LAUNCH_INTENT = "voice_launch_intent"; + + /** + * Volume key controls ringtone or media sound stream + * @hide + */ + public static final String VOLUME_KEYS_CONTROL_RING_STREAM = + "volume_keys_control_ring_stream"; + /** * Settings to backup. This is here so that it's in the same place as the settings * keys and easy to update. @@ -3270,7 +3587,12 @@ public final class Settings { VIBRATE_WHEN_RINGING, RINGTONE, LOCK_TO_APP_ENABLED, - NOTIFICATION_SOUND + NOTIFICATION_SOUND, + PHONE_BLACKLIST_ENABLED, + PHONE_BLACKLIST_NOTIFY_ENABLED, + PHONE_BLACKLIST_PRIVATE_NUMBER_MODE, + PHONE_BLACKLIST_UNKNOWN_NUMBER_MODE, + PHONE_BLACKLIST_REGEX_ENABLED, }; /** @@ -3323,6 +3645,7 @@ public final class Settings { PUBLIC_SETTINGS.add(SOUND_EFFECTS_ENABLED); PUBLIC_SETTINGS.add(HAPTIC_FEEDBACK_ENABLED); PUBLIC_SETTINGS.add(SHOW_WEB_SUGGESTIONS); + PUBLIC_SETTINGS.add(POWER_OFF_ALARM_MODE); } /** @@ -3448,6 +3771,7 @@ public final class Settings { VALIDATORS.put(WIFI_STATIC_NETMASK, WIFI_STATIC_NETMASK_VALIDATOR); VALIDATORS.put(WIFI_STATIC_DNS1, WIFI_STATIC_DNS1_VALIDATOR); VALIDATORS.put(WIFI_STATIC_DNS2, WIFI_STATIC_DNS2_VALIDATOR); + VALIDATORS.put(POWER_OFF_ALARM_MODE, POWER_OFF_ALARM_MODE_VALIDATOR); } /** @@ -3756,6 +4080,9 @@ public final class Settings { MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_ENABLED); MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_VISIBLE); MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED); + MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_SIZE); + MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_DOTS_VISIBLE); + MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_SHOW_ERROR_PATH); MOVED_TO_GLOBAL = new HashSet<String>(); MOVED_TO_GLOBAL.add(Settings.Global.ADB_ENABLED); @@ -3872,6 +4199,44 @@ public final class Settings { MOVED_TO_GLOBAL.add(Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY); } + /** + * Put a delimited list as a string + * @param resolver to access the database with + * @param name to store + * @param delimiter to split + * @param list to join and store + * @hide + */ + public static void putListAsDelimitedString(ContentResolver resolver, String name, + String delimiter, List<String> list) { + String store = TextUtils.join(delimiter, list); + putString(resolver, name, store); + } + + /** + * Get a delimited string returned as a list + * @param resolver to access the database with + * @param name to store + * @param delimiter to split the list with + * @return list of strings for a specific Settings.Secure item + * @hide + */ + public static List<String> getDelimitedStringAsList(ContentResolver resolver, String name, + String delimiter) { + String baseString = getString(resolver, name); + List<String> list = new ArrayList<String>(); + if (!TextUtils.isEmpty(baseString)) { + final String[] array = TextUtils.split(baseString, Pattern.quote(delimiter)); + for (String item : array) { + if (TextUtils.isEmpty(item)) { + continue; + } + list.add(item); + } + } + return list; + } + /** @hide */ public static void getMovedToGlobalSettings(Set<String> outKeySet) { outKeySet.addAll(MOVED_TO_GLOBAL); @@ -4488,6 +4853,24 @@ public final class Settings { LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled"; /** + * Determines the width and height of the LockPatternView widget + * @hide + */ + public static final String LOCK_PATTERN_SIZE = "lock_pattern_size"; + + /** + * Whether lock pattern will show dots (0 = false, 1 = true) + * @hide + */ + public static final String LOCK_DOTS_VISIBLE = "lock_pattern_dotsvisible"; + + /** + * Whether lockscreen error pattern is visible (0 = false, 1 = true) + * @hide + */ + public static final String LOCK_SHOW_ERROR_PATH = "lock_pattern_show_error_path"; + + /** * This preference allows the device to be locked given time after screen goes off, * subject to current DeviceAdmin policy limits. * @hide @@ -4588,6 +4971,12 @@ public final class Settings { public static final String PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url"; /** + * Known good originating source sms addresses + * @hide + */ + public static final String PROTECTED_SMS_ADDRESSES = "protected_sms_addresses"; + + /** * Settings classname to launch when Settings is clicked from All * Applications. Needed because of user testing between the old * and new Settings apps. @@ -5155,6 +5544,12 @@ public final class Settings { "connectivity_release_pending_intent_delay_ms"; /** + * Whether the Wimax should be on. Only the WiMAX service should touch this. + * @hide + */ + public static final String WIMAX_ON = "wimax_on"; + + /** * Whether background data usage is allowed. * * @deprecated As of {@link VERSION_CODES#ICE_CREAM_SANDWICH}, @@ -5692,6 +6087,12 @@ public final class Settings { public static final String ASSISTANT = "assistant"; /** + * Default theme to use. If empty, use system. + * @hide + */ + public static final String DEFAULT_THEME_PACKAGE = "default_theme_package"; + + /** * Whether the camera launch gesture should be disabled. * * @hide @@ -5708,6 +6109,12 @@ public final class Settings { "camera_double_tap_power_gesture_disabled"; /** + * Boolean value whether to link ringtone and notification volume + * @hide + */ + public static final String VOLUME_LINK_NOTIFICATION = "volume_link_notification"; + + /** * This are the settings to be backed up. * * NOTE: Settings are backed up and restored in the order they appear @@ -5763,7 +6170,7 @@ public final class Settings { MOUNT_UMS_NOTIFY_ENABLED, SLEEP_TIMEOUT, DOUBLE_TAP_TO_WAKE, - CAMERA_GESTURE_DISABLED, + CAMERA_GESTURE_DISABLED }; /** @@ -6030,6 +6437,13 @@ public final class Settings { public static final String AIRPLANE_MODE_TOGGLEABLE_RADIOS = "airplane_mode_toggleable_radios"; /** + * A Long representing a bitmap of profiles that should be disabled when bluetooth starts. + * See {@link android.bluetooth.BluetoothProfile}. + * {@hide} + */ + public static final String BLUETOOTH_DISABLED_PROFILES = "bluetooth_disabled_profiles"; + + /** * A semi-colon separated list of Bluetooth interoperability workarounds. * Each entry is a partial Bluetooth device address string and an integer representing * the feature to be disabled, separated by a comma. The integer must correspond @@ -6182,6 +6596,14 @@ public final class Settings { public static final String ADB_ENABLED = "adb_enabled"; /** + * String to contain power menu actions + * @deprecated Use {@link CMSettings.Secure#POWER_MENU_ACTIONS} instead + * @hide + */ + @Deprecated + public static final String POWER_MENU_ACTIONS = "power_menu_actions"; + + /** * Whether Views are allowed to save their attribute data. * @hide */ @@ -7239,6 +7661,9 @@ public final class Settings { BLUETOOTH_A2DP_SINK_PRIORITY_PREFIX = "bluetooth_a2dp_sink_priority_"; /** {@hide} */ public static final String + BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX = "bluetooth_a2dp_src_priority_"; + /** {@hide} */ + public static final String BLUETOOTH_INPUT_DEVICE_PRIORITY_PREFIX = "bluetooth_input_device_priority_"; /** {@hide} */ public static final String @@ -7341,6 +7766,14 @@ public final class Settings { } /** + * Get the key that retrieves a bluetooth a2dp src's priority. + * @hide + */ + public static final String getBluetoothA2dpSrcPriorityKey(String address) { + return BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT); + } + + /** * Get the key that retrieves a bluetooth Input Device's priority. * @hide */ @@ -7571,13 +8004,22 @@ public final class Settings { /** * Defines global runtime overrides to window policy. * - * See {@link com.android.server.policy.PolicyControl} for value format. + * See {@link android.view.WindowManagerPolicyControl} for value format. * * @hide */ public static final String POLICY_CONTROL = "policy_control"; /** + * Defines global runtime overrides to window policy style. + * + * See {@link android.view.WindowManagerPolicyControl} for value definitions. + * + * @hide + */ + public static final String POLICY_CONTROL_STYLE = "policy_control_style"; + + /** * Defines global zen mode. ZEN_MODE_OFF, ZEN_MODE_IMPORTANT_INTERRUPTIONS, * or ZEN_MODE_NO_INTERRUPTIONS. * @@ -7667,7 +8109,7 @@ public final class Settings { public static final String REQUIRE_PASSWORD_TO_DECRYPT = "require_password_to_decrypt"; /** - * Whether the Volte/VT is enabled + * Whether the Volte is enabled * <p> * Type: int (0 for false, 1 for true) * @hide @@ -7675,6 +8117,15 @@ public final class Settings { public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled"; /** + * Whether VT (Video Telephony over IMS) is enabled + * <p> + * Type: int (0 for false, 1 for true) + * + * @hide + */ + public static final String VT_IMS_ENABLED = "vt_ims_enabled"; + + /** * Whether WFC is enabled * <p> * Type: int (0 for false, 1 for true) @@ -7711,6 +8162,13 @@ public final class Settings { public static final String LTE_SERVICE_FORCED = "lte_service_forced"; /** + * Whether to ignore the representation of outgoing calls set by the network. + * + * @hide + */ + public static final String CONNECTED_LINE_IDENTIFICATION = "connected_line_identification"; + + /** * Settings to backup. This is here so that it's in the same place as the settings * keys and easy to update. * @@ -7757,6 +8215,7 @@ public final class Settings { static { MOVED_TO_SECURE = new HashSet<String>(1); MOVED_TO_SECURE.add(Settings.Global.INSTALL_NON_MARKET_APPS); + MOVED_TO_SECURE.add(Settings.Global.POWER_MENU_ACTIONS); } /** @hide */ @@ -7765,6 +8224,44 @@ public final class Settings { } /** + * Put a delimited list as a string + * @param resolver to access the database with + * @param name to store + * @param delimiter to split + * @param list to join and store + * @hide + */ + public static void putListAsDelimitedString(ContentResolver resolver, String name, + String delimiter, List<String> list) { + String store = TextUtils.join(delimiter, list); + putString(resolver, name, store); + } + + /** + * Get a delimited string returned as a list + * @param resolver to access the database with + * @param name to store + * @param delimiter to split the list with + * @return list of strings for a specific Settings.Secure item + * @hide + */ + public static List<String> getDelimitedStringAsList(ContentResolver resolver, String name, + String delimiter) { + String baseString = getString(resolver, name); + List<String> list = new ArrayList<String>(); + if (!TextUtils.isEmpty(baseString)) { + final String[] array = TextUtils.split(baseString, Pattern.quote(delimiter)); + for (String item : array) { + if (TextUtils.isEmpty(item)) { + continue; + } + list.add(item); + } + } + return list; + } + + /** * Look up a name in the database. * @param resolver to access the database with * @param name to look up in the table @@ -7820,6 +8317,12 @@ public final class Settings { * @return the corresponding content URI, or null if not present */ public static Uri getUriFor(String name) { + if (MOVED_TO_SECURE.contains(name)) { + Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.Global" + + " to android.provider.Settings.Secure, returning Secure URI."); + return Secure.getUriFor(Secure.CONTENT_URI, name); + } + return getUriFor(CONTENT_URI, name); } @@ -8091,6 +8594,13 @@ public final class Settings { * @hide */ public static final String CONTACT_METADATA_SYNC = "contact_metadata_sync"; + + /** + * Whether to enable cellular on boot. + * The value 1 - enable, 0 - disable + * @hide + */ + public static final String ENABLE_CELLULAR_ON_BOOT = "enable_cellular_on_boot"; } /** diff --git a/core/java/android/service/dreams/DreamManagerInternal.java b/core/java/android/service/dreams/DreamManagerInternal.java index ff7cef9..c750b5e 100644 --- a/core/java/android/service/dreams/DreamManagerInternal.java +++ b/core/java/android/service/dreams/DreamManagerInternal.java @@ -42,4 +42,9 @@ public abstract class DreamManagerInternal { * Called by the power manager to determine whether a dream is running. */ public abstract boolean isDreaming(); + + /** + * Called by the power manager to determine whether the dream has gone to doze mode. + */ + public abstract boolean isDozing(); } diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl index be3f3b3..78dda0c 100644 --- a/core/java/android/service/dreams/IDreamManager.aidl +++ b/core/java/android/service/dreams/IDreamManager.aidl @@ -30,7 +30,10 @@ interface IDreamManager { ComponentName getDefaultDreamComponent(); void testDream(in ComponentName componentName); boolean isDreaming(); + boolean isDozing(); void finishSelf(in IBinder token, boolean immediate); void startDozing(in IBinder token, int screenState, int screenBrightness); void stopDozing(in IBinder token); + void setLidState(int state); + int getLidState(); } diff --git a/core/java/android/service/gesture/IGestureService.aidl b/core/java/android/service/gesture/IGestureService.aidl new file mode 100644 index 0000000..1944d50 --- /dev/null +++ b/core/java/android/service/gesture/IGestureService.aidl @@ -0,0 +1,11 @@ +package android.service.gesture; + +import android.app.PendingIntent; + +/** @hide */ +interface IGestureService { + + void setOnLongPressPendingIntent(in PendingIntent pendingIntent); + void setOnDoubleClickPendingIntent(in PendingIntent pendingIntent); + +} diff --git a/core/java/android/service/voice/VoiceInteractionServiceInfo.java b/core/java/android/service/voice/VoiceInteractionServiceInfo.java index 463eb5b..8393f7e 100644 --- a/core/java/android/service/voice/VoiceInteractionServiceInfo.java +++ b/core/java/android/service/voice/VoiceInteractionServiceInfo.java @@ -58,6 +58,10 @@ public class VoiceInteractionServiceInfo { } public VoiceInteractionServiceInfo(PackageManager pm, ServiceInfo si) { + if (si == null) { + mParseError = "Service not available"; + return; + } if (!Manifest.permission.BIND_VOICE_INTERACTION.equals(si.permission)) { mParseError = "Service does not require permission " + Manifest.permission.BIND_VOICE_INTERACTION; diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java index 88e2ede..3eb24e4 100644 --- a/core/java/android/speech/SpeechRecognizer.java +++ b/core/java/android/speech/SpeechRecognizer.java @@ -260,6 +260,7 @@ public class SpeechRecognizer { * not set explicitly, default values will be used by the recognizer. */ public void startListening(final Intent recognizerIntent) { + android.util.SeempLog.record(72); if (recognizerIntent == null) { throw new IllegalArgumentException("intent must not be null"); } diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index fa347b9..22ad634 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -1130,20 +1130,31 @@ public abstract class Layout { */ public int getOffsetForHorizontal(int line, float horiz) { // TODO: use Paint.getOffsetForAdvance to avoid binary search - int max = getLineEnd(line) - 1; - int min = getLineStart(line); + final int lineEndOffset = getLineEnd(line); + final int lineStartOffset = getLineStart(line); + Directions dirs = getLineDirections(line); - if (line == getLineCount() - 1) - max++; + TextLine tl = TextLine.obtain(); + // XXX: we don't care about tabs as we just use TextLine#getOffsetToLeftRightOf here. + tl.set(mPaint, mText, lineStartOffset, lineEndOffset, getParagraphDirection(line), dirs, + false, null); - int best = min; + final int max; + if (line == getLineCount() - 1) { + max = lineEndOffset; + } else { + max = tl.getOffsetToLeftRightOf(lineEndOffset - lineStartOffset, + !isRtlCharAt(lineEndOffset - 1)) + lineStartOffset; + } + int best = lineStartOffset; float bestdist = Math.abs(getPrimaryHorizontal(best) - horiz); for (int i = 0; i < dirs.mDirections.length; i += 2) { - int here = min + dirs.mDirections[i]; + int here = lineStartOffset + dirs.mDirections[i]; int there = here + (dirs.mDirections[i+1] & RUN_LENGTH_MASK); - int swap = (dirs.mDirections[i+1] & RUN_RTL_FLAG) != 0 ? -1 : 1; + boolean isRtl = (dirs.mDirections[i+1] & RUN_RTL_FLAG) != 0; + int swap = isRtl ? -1 : 1; if (there > max) there = max; @@ -1163,23 +1174,23 @@ public abstract class Layout { low = here + 1; if (low < there) { - low = getOffsetAtStartOf(low); - - float dist = Math.abs(getPrimaryHorizontal(low) - horiz); - - int aft = TextUtils.getOffsetAfter(mText, low); - if (aft < there) { - float other = Math.abs(getPrimaryHorizontal(aft) - horiz); - - if (other < dist) { - dist = other; - low = aft; + int aft = tl.getOffsetToLeftRightOf(low - lineStartOffset, isRtl) + lineStartOffset; + low = tl.getOffsetToLeftRightOf(aft - lineStartOffset, !isRtl) + lineStartOffset; + if (low >= here && low < there) { + float dist = Math.abs(getPrimaryHorizontal(low) - horiz); + if (aft < there) { + float other = Math.abs(getPrimaryHorizontal(aft) - horiz); + + if (other < dist) { + dist = other; + low = aft; + } } - } - if (dist < bestdist) { - bestdist = dist; - best = low; + if (dist < bestdist) { + bestdist = dist; + best = low; + } } } @@ -1198,6 +1209,7 @@ public abstract class Layout { best = max; } + TextLine.recycle(tl); return best; } diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index 39e8694..3592187 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -718,13 +718,14 @@ class TextLine { * @param bottom the bottom of the line * @param fmi receives metrics information, can be null * @param needWidth true if the width of the run is needed + * @param offset the offset for the purpose of measuring * @return the signed width of the run based on the run direction; only * valid if needWidth is true */ private float handleText(TextPaint wp, int start, int end, int contextStart, int contextEnd, boolean runIsRtl, Canvas c, float x, int top, int y, int bottom, - FontMetricsInt fmi, boolean needWidth) { + FontMetricsInt fmi, boolean needWidth, int offset) { // Get metrics first (even for empty strings or "0" width runs) if (fmi != null) { @@ -742,11 +743,11 @@ class TextLine { if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineColor != 0 || runIsRtl))) { if (mCharsValid) { ret = wp.getRunAdvance(mChars, start, end, contextStart, contextEnd, - runIsRtl, end); + runIsRtl, offset); } else { int delta = mStart; ret = wp.getRunAdvance(mText, delta + start, delta + end, - delta + contextStart, delta + contextEnd, runIsRtl, delta + end); + delta + contextStart, delta + contextEnd, runIsRtl, delta + offset); } } @@ -895,8 +896,8 @@ class TextLine { TextPaint wp = mWorkPaint; wp.set(mPaint); final int mlimit = measureLimit; - return handleText(wp, start, mlimit, start, limit, runIsRtl, c, x, top, - y, bottom, fmi, needWidth || mlimit < measureLimit); + return handleText(wp, start, limit, start, limit, runIsRtl, c, x, top, + y, bottom, fmi, needWidth || mlimit < measureLimit, mlimit); } mMetricAffectingSpanSpanSet.init(mSpanned, mStart + start, mStart + limit); @@ -940,13 +941,14 @@ class TextLine { } for (int j = i, jnext; j < mlimit; j = jnext) { - jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + mlimit) - + jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + inext) - mStart; + int offset = Math.min(jnext, mlimit); wp.set(mPaint); for (int k = 0; k < mCharacterStyleSpanSet.numberOfSpans; k++) { // Intentionally using >= and <= as explained above - if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + jnext) || + if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + offset) || (mCharacterStyleSpanSet.spanEnds[k] <= mStart + j)) continue; CharacterStyle span = mCharacterStyleSpanSet.spans[k]; @@ -958,7 +960,7 @@ class TextLine { wp.setHyphenEdit(0); } x += handleText(wp, j, jnext, i, inext, runIsRtl, c, x, - top, y, bottom, fmi, needWidth || jnext < measureLimit); + top, y, bottom, fmi, needWidth || jnext < measureLimit, offset); } } diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index d8f7158..75fd2da 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -1754,19 +1754,21 @@ public class TextUtils { * Be careful: this code will need to be updated when vertical scripts will be supported */ public static int getLayoutDirectionFromLocale(Locale locale) { + boolean mirror = SystemProperties.getBoolean(Settings.Global.DEVELOPMENT_FORCE_RTL, false); if (locale != null && !locale.equals(Locale.ROOT)) { final String scriptSubtag = ICU.addLikelySubtags(locale).getScript(); if (scriptSubtag == null) return getLayoutDirectionFromFirstChar(locale); if (scriptSubtag.equalsIgnoreCase(ARAB_SCRIPT_SUBTAG) || scriptSubtag.equalsIgnoreCase(HEBR_SCRIPT_SUBTAG)) { - return View.LAYOUT_DIRECTION_RTL; + // If forcing into RTL layout mode and language is RTL + // return LTR as default, else RTL + return mirror ? View.LAYOUT_DIRECTION_LTR : View.LAYOUT_DIRECTION_RTL; } } - // If forcing into RTL layout mode, return RTL as default, else LTR - return SystemProperties.getBoolean(Settings.Global.DEVELOPMENT_FORCE_RTL, false) - ? View.LAYOUT_DIRECTION_RTL - : View.LAYOUT_DIRECTION_LTR; + // If forcing into RTL layout mode and language is LTR + // return RTL as default, else LTR + return mirror ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR; } /** diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java index 3ed37b3..998fa9d 100755 --- a/core/java/android/text/format/DateFormat.java +++ b/core/java/android/text/format/DateFormat.java @@ -177,12 +177,23 @@ public class DateFormat { * @hide */ public static boolean is24HourFormat(Context context, int userHandle) { - String value = Settings.System.getStringForUser(context.getContentResolver(), + String setting = Settings.System.getStringForUser(context.getContentResolver(), Settings.System.TIME_12_24, userHandle); + Locale locale = context.getResources().getConfiguration().locale; + return is24HourFormat(setting, locale); + } - if (value == null) { - Locale locale = context.getResources().getConfiguration().locale; - + /** + * Returns true if user preference with the given user handle is set to 24-hour format. + * @param setting value of the TIME_12_24 system setting, which may be null + * @param locale current default locale for this device + * @param userHandle the user handle of the user to query. + * @return true if 24 hour time format is selected, false otherwise. + * + * @hide + */ + public static boolean is24HourFormat(String setting, Locale locale) { + if (setting == null) { synchronized (sLocaleLock) { if (sIs24HourLocale != null && sIs24HourLocale.equals(locale)) { return sIs24Hour; @@ -197,23 +208,23 @@ public class DateFormat { String pattern = sdf.toPattern(); if (pattern.indexOf('H') >= 0) { - value = "24"; + setting = "24"; } else { - value = "12"; + setting = "12"; } } else { - value = "12"; + setting = "12"; } synchronized (sLocaleLock) { sIs24HourLocale = locale; - sIs24Hour = value.equals("24"); + sIs24Hour = setting.equals("24"); } return sIs24Hour; } - return value.equals("24"); + return setting.equals("24"); } /** diff --git a/core/java/android/text/method/MetaKeyKeyListener.java b/core/java/android/text/method/MetaKeyKeyListener.java index e9db5fd..569ca60 100644 --- a/core/java/android/text/method/MetaKeyKeyListener.java +++ b/core/java/android/text/method/MetaKeyKeyListener.java @@ -23,6 +23,9 @@ import android.text.Spanned; import android.view.KeyEvent; import android.view.View; import android.view.KeyCharacterMap; +import android.os.IPowerManager; +import android.os.RemoteException; +import android.os.ServiceManager; /** * This base class encapsulates the behavior for tracking the state of @@ -273,6 +276,14 @@ public abstract class MetaKeyKeyListener { adjust(content, CAP); adjust(content, ALT); adjust(content, SYM); + try { + IPowerManager power = IPowerManager.Stub.asInterface( + ServiceManager.getService("power")); + if (getMetaState(content, META_SHIFT_ON) <= 0) + power.setKeyboardLight(false, 1); + if (getMetaState(content, META_ALT_ON) <= 0) + power.setKeyboardLight(false, 2); + } catch (RemoteException doe) {} } /** @@ -325,12 +336,32 @@ public abstract class MetaKeyKeyListener { public boolean onKeyDown(View view, Editable content, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) { press(content, CAP); + try { + IPowerManager power = IPowerManager.Stub.asInterface( + ServiceManager.getService("power")); + int state = content.getSpanFlags(CAP); + if (state == PRESSED || state == LOCKED) { + power.setKeyboardLight(true, 1); + } else { + power.setKeyboardLight(false, 1); + } + } catch (RemoteException doe) {} return true; } if (keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT || keyCode == KeyEvent.KEYCODE_NUM) { press(content, ALT); + try { + IPowerManager power = IPowerManager.Stub.asInterface( + ServiceManager.getService("power")); + int state = content.getSpanFlags(ALT); + if (state == PRESSED || state == LOCKED) { + power.setKeyboardLight(true, 2); + } else { + power.setKeyboardLight(false, 2); + } + } catch (RemoteException doe) {} return true; } diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java index 9a69600..ee959e2 100644 --- a/core/java/android/util/DisplayMetrics.java +++ b/core/java/android/util/DisplayMetrics.java @@ -16,6 +16,7 @@ package android.util; +import android.graphics.Bitmap; import android.os.SystemProperties; @@ -138,7 +139,20 @@ public class DisplayMetrics { * density for a display in {@link #densityDpi}. */ @Deprecated - public static int DENSITY_DEVICE = getDeviceDensity(); + public static int DENSITY_DEVICE; + + /** @hide */ + public static int DENSITY_PREFERRED; + + /** @hide */ + public static int DENSITY_DEVICE_DEFAULT; + + static { + DENSITY_DEVICE = SystemProperties.getInt("qemu.sf.lcd_density", SystemProperties + .getInt("ro.sf.lcd_density", DENSITY_DEFAULT)); + DENSITY_DEVICE_DEFAULT = DENSITY_DEVICE; + DENSITY_PREFERRED = SystemProperties.getInt("persist.sys.lcd_density", DENSITY_DEVICE); + } /** * The absolute width of the display in pixels. @@ -229,6 +243,24 @@ public class DisplayMetrics { */ public float noncompatYdpi; + /** @hide */ + public void setDensity(int inDensity) { + density = inDensity / (float) DENSITY_DEFAULT; + densityDpi = inDensity; + scaledDensity = density; + xdpi = inDensity; + ydpi = inDensity; + + noncompatDensity = density; + noncompatDensityDpi = densityDpi; + noncompatScaledDensity = scaledDensity; + noncompatXdpi = xdpi; + noncompatYdpi = ydpi; + + DENSITY_DEVICE = inDensity; + Bitmap.setDefaultDensity(inDensity); + } + public DisplayMetrics() { } @@ -319,13 +351,4 @@ public class DisplayMetrics { ", height=" + heightPixels + ", scaledDensity=" + scaledDensity + ", xdpi=" + xdpi + ", ydpi=" + ydpi + "}"; } - - private static int getDeviceDensity() { - // qemu.sf.lcd_density can be used to override ro.sf.lcd_density - // when running in the emulator, allowing for dynamic configurations. - // The reason for this is that ro.sf.lcd_density is write-once and is - // set by the init process when it parses build.prop before anything else. - return SystemProperties.getInt("qemu.sf.lcd_density", - SystemProperties.getInt("ro.sf.lcd_density", DENSITY_DEFAULT)); - } } diff --git a/core/java/android/util/Patterns.java b/core/java/android/util/Patterns.java index 2cc91b9..f1b3feb 100644 --- a/core/java/android/util/Patterns.java +++ b/core/java/android/util/Patterns.java @@ -125,15 +125,35 @@ public class Patterns { + "|[1-9][0-9]|[0-9]))"); /** + * Match the characters without containing chinese characters + * @hide + */ + private static final String GOOD_IRI_HOST_CHAR = + "a-zA-Z0-9\u00A0-\u2FFF\u3040-\u4DFF\u9FA6-\uD7FF" + + "\uF900-\uFDCF\uFDF0-\uFEFF"; + + /** * RFC 1035 Section 2.3.4 limits the labels to a maximum 63 octets. */ - private static final String IRI - = "[" + GOOD_IRI_CHAR + "]([" + GOOD_IRI_CHAR + "\\-]{0,61}[" + GOOD_IRI_CHAR + "]){0,1}"; + private static final String IRI = + "[" + GOOD_IRI_HOST_CHAR + "]([" + GOOD_IRI_HOST_CHAR + "\\-_~]{0,61}[" + + GOOD_IRI_HOST_CHAR + "]){0,1}"; private static final String GOOD_GTLD_CHAR = - "a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF"; + "a-zA-Z\u00A0-\u2FFF\u3040-\u4DFF\u9FA6-\uD7FF" + + "\uF900-\uFDCF\uFDF0-\uFEFF"; private static final String GTLD = "[" + GOOD_GTLD_CHAR + "]{2,63}"; private static final String HOST_NAME = "(" + IRI + "\\.)+" + GTLD; + // Halfwidth and fullwidth forms + private static final String HALF_FULL_WIDTH_CHAR = "\uFF00-\uFFEF"; + // Symbols and punctuation + private static final String SYMBOLS_PUNCTUATION_CHAR = "\u3000-\u303F"; + // Chinese characters + private static final String CHINESE_CHAR = "\u4E00-\u9FA5"; + // Forbidden characters, should remove from URL, + private static final String FORBIDDEN_CHAR = + "[" + SYMBOLS_PUNCTUATION_CHAR + CHINESE_CHAR + + HALF_FULL_WIDTH_CHAR + "]"; public static final Pattern DOMAIN_NAME = Pattern.compile("(" + HOST_NAME + "|" + IP_ADDRESS + ")"); @@ -149,11 +169,15 @@ public class Patterns { + "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@)?)?" + "(?:" + DOMAIN_NAME + ")" + "(?:\\:\\d{1,5})?)" // plus option port number - + "(\\/(?:(?:[" + GOOD_IRI_CHAR + "\\;\\/\\?\\:\\@\\&\\=\\#\\~" // plus option query params - + "\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])|(?:\\%[a-fA-F0-9]{2}))*)?" - + "(?:\\b|$)"); // and finally, a word boundary or end of - // input. This is to stop foo.sure from - // matching as foo.su + + "(\\/(?:(?:[" + GOOD_IRI_HOST_CHAR + + "\\;\\/\\?\\:\\@\\&\\=\\#\\~" // plus option query params + + "\\-\\.\\+\\!\\*\\'\\(\\)\\_])|(?:\\,[" + GOOD_IRI_HOST_CHAR + + "])|(?:\\%[a-fA-F0-9]{2}))*)?" + + "(?:(?=" + FORBIDDEN_CHAR + + ")|\\b|$)"); + // and finally, a word boundary or end of input. This is to stop + // foo.sure from matching as foo.su + // also should remove forbidden characters from end of URL. public static final Pattern EMAIL_ADDRESS = Pattern.compile( diff --git a/core/java/android/util/SeempLog.java b/core/java/android/util/SeempLog.java new file mode 100644 index 0000000..6e6cce6 --- /dev/null +++ b/core/java/android/util/SeempLog.java @@ -0,0 +1,758 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package android.util; + +import com.android.internal.os.RuntimeInit; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.net.UnknownHostException; +import java.util.Hashtable; +import java.util.Map; +import java.util.List; +import java.util.Iterator; +import android.util.Log; +import android.provider.Settings; + +/** + * SeempLog + * + * @hide + */ +public final class SeempLog { + private SeempLog() { + } + + /** + * Send a log message to the seemp log. + * @param api The api triggering this message. + */ + public static int record(int api) { + return seemp_println_native(api, ""); + } + + /** + * Send a log message to the seemp log. + * @param api The api triggering this message. + * @param msg The message you would like logged. + */ + public static int record_str(int api, String msg) { + if ( msg != null ) { + return seemp_println_native(api, msg); + } + else { + return seemp_println_native(api, ""); + } + } + + public static int record_sensor(int api, + android.hardware.Sensor sensor) { + if ( sensor != null ) { + return seemp_println_native(api, "sensor="+sensor.getType()); + } + else { + return seemp_println_native(api, "sensor=-1"); + } + } + + public static int record_sensor_rate(int api, + android.hardware.Sensor sensor, int rate) { + if ( sensor != null ) { + return seemp_println_native(api, + "sensor="+sensor.getType() + ",rate="+rate); + } + else { + return seemp_println_native(api, "sensor=-1,rate=" + rate); + } + } + + public static int record_uri(int api, android.net.Uri uri) { + if ( uri != null ) { + return seemp_println_native(api, "uri, " + uri.toString()); + } + else { + return seemp_println_native(api, "uri, null" ); + } + } + + public static int record_vg_layout(int api, + android.view.ViewGroup.LayoutParams params) { + try { + android.view.WindowManager.LayoutParams p = + (android.view.WindowManager.LayoutParams) params; + if ( p != null ) { + return seemp_println_native(api, + "window_type=" + p.type + ",window_flag=" + p.flags); + } + else { + return seemp_println_native(api, ""); + } + } catch (ClassCastException cce) { + return seemp_println_native(api, ""); + } + } + + /** @hide */ public static native int seemp_println_native(int api, String msg); + + public static final int SEEMP_API_android_provider_Settings__get_ANDROID_ID_ = 7; + public static final int SEEMP_API_android_provider_Settings__get_ACCELEROMETER_ROTATION_ = 96; + public static final int SEEMP_API_android_provider_Settings__get_USER_ROTATION_ = 97; + public static final int SEEMP_API_android_provider_Settings__get_ADB_ENABLED_ = 98; + public static final int SEEMP_API_android_provider_Settings__get_DEBUG_APP_ = 99; + public static final int SEEMP_API_android_provider_Settings__get_WAIT_FOR_DEBUGGER_ = 100; + public static final int SEEMP_API_android_provider_Settings__get_AIRPLANE_MODE_ON_ = 101; + public static final int SEEMP_API_android_provider_Settings__get_AIRPLANE_MODE_RADIOS_ = 102; + public static final int SEEMP_API_android_provider_Settings__get_ALARM_ALERT_ = 103; + public static final int SEEMP_API_android_provider_Settings__get_NEXT_ALARM_FORMATTED_ = 104; + public static final int SEEMP_API_android_provider_Settings__get_ALWAYS_FINISH_ACTIVITIES_ = 105; + public static final int SEEMP_API_android_provider_Settings__get_LOGGING_ID_ = 106; + public static final int SEEMP_API_android_provider_Settings__get_ANIMATOR_DURATION_SCALE_ = 107; + public static final int SEEMP_API_android_provider_Settings__get_WINDOW_ANIMATION_SCALE_ = 108; + public static final int SEEMP_API_android_provider_Settings__get_FONT_SCALE_ = 109; + public static final int SEEMP_API_android_provider_Settings__get_SCREEN_BRIGHTNESS_ = 110; + public static final int SEEMP_API_android_provider_Settings__get_SCREEN_BRIGHTNESS_MODE_ = 111; + public static final int SEEMP_API_android_provider_Settings__get_SCREEN_BRIGHTNESS_MODE_AUTOMATIC_ = 112; + public static final int SEEMP_API_android_provider_Settings__get_SCREEN_BRIGHTNESS_MODE_MANUAL_ = 113; + public static final int SEEMP_API_android_provider_Settings__get_SCREEN_OFF_TIMEOUT_ = 114; + public static final int SEEMP_API_android_provider_Settings__get_DIM_SCREEN_ = 115; + public static final int SEEMP_API_android_provider_Settings__get_TRANSITION_ANIMATION_SCALE_ = 116; + public static final int SEEMP_API_android_provider_Settings__get_STAY_ON_WHILE_PLUGGED_IN_ = 117; + public static final int SEEMP_API_android_provider_Settings__get_WALLPAPER_ACTIVITY_ = 118; + public static final int SEEMP_API_android_provider_Settings__get_SHOW_PROCESSES_ = 119; + public static final int SEEMP_API_android_provider_Settings__get_SHOW_WEB_SUGGESTIONS_ = 120; + public static final int SEEMP_API_android_provider_Settings__get_SHOW_GTALK_SERVICE_STATUS_ = 121; + public static final int SEEMP_API_android_provider_Settings__get_USE_GOOGLE_MAIL_ = 122; + public static final int SEEMP_API_android_provider_Settings__get_AUTO_TIME_ = 123; + public static final int SEEMP_API_android_provider_Settings__get_AUTO_TIME_ZONE_ = 124; + public static final int SEEMP_API_android_provider_Settings__get_DATE_FORMAT_ = 125; + public static final int SEEMP_API_android_provider_Settings__get_TIME_12_24_ = 126; + public static final int SEEMP_API_android_provider_Settings__get_BLUETOOTH_DISCOVERABILITY_ = 127; + public static final int SEEMP_API_android_provider_Settings__get_BLUETOOTH_DISCOVERABILITY_TIMEOUT_ = 128; + public static final int SEEMP_API_android_provider_Settings__get_BLUETOOTH_ON_ = 129; + public static final int SEEMP_API_android_provider_Settings__get_DEVICE_PROVISIONED_ = 130; + public static final int SEEMP_API_android_provider_Settings__get_SETUP_WIZARD_HAS_RUN_ = 131; + public static final int SEEMP_API_android_provider_Settings__get_DTMF_TONE_WHEN_DIALING_ = 132; + public static final int SEEMP_API_android_provider_Settings__get_END_BUTTON_BEHAVIOR_ = 133; + public static final int SEEMP_API_android_provider_Settings__get_RINGTONE_ = 134; + public static final int SEEMP_API_android_provider_Settings__get_MODE_RINGER_ = 135; + public static final int SEEMP_API_android_provider_Settings__get_INSTALL_NON_MARKET_APPS_ = 136; + public static final int SEEMP_API_android_provider_Settings__get_LOCATION_PROVIDERS_ALLOWED_ = 137; + public static final int SEEMP_API_android_provider_Settings__get_LOCK_PATTERN_ENABLED_ = 138; + public static final int SEEMP_API_android_provider_Settings__get_LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED_ = 139; + public static final int SEEMP_API_android_provider_Settings__get_LOCK_PATTERN_VISIBLE_ = 140; + public static final int SEEMP_API_android_provider_Settings__get_NETWORK_PREFERENCE_ = 141; + public static final int SEEMP_API_android_provider_Settings__get_DATA_ROAMING_ = 142; + public static final int SEEMP_API_android_provider_Settings__get_HTTP_PROXY_ = 143; + public static final int SEEMP_API_android_provider_Settings__get_PARENTAL_CONTROL_ENABLED_ = 144; + public static final int SEEMP_API_android_provider_Settings__get_PARENTAL_CONTROL_LAST_UPDATE_ = 145; + public static final int SEEMP_API_android_provider_Settings__get_PARENTAL_CONTROL_REDIRECT_URL_ = 146; + public static final int SEEMP_API_android_provider_Settings__get_RADIO_BLUETOOTH_ = 147; + public static final int SEEMP_API_android_provider_Settings__get_RADIO_CELL_ = 148; + public static final int SEEMP_API_android_provider_Settings__get_RADIO_NFC_ = 149; + public static final int SEEMP_API_android_provider_Settings__get_RADIO_WIFI_ = 150; + public static final int SEEMP_API_android_provider_Settings__get_SYS_PROP_SETTING_VERSION_ = 151; + public static final int SEEMP_API_android_provider_Settings__get_SETTINGS_CLASSNAME_ = 152; + public static final int SEEMP_API_android_provider_Settings__get_TEXT_AUTO_CAPS_ = 153; + public static final int SEEMP_API_android_provider_Settings__get_TEXT_AUTO_PUNCTUATE_ = 154; + public static final int SEEMP_API_android_provider_Settings__get_TEXT_AUTO_REPLACE_ = 155; + public static final int SEEMP_API_android_provider_Settings__get_TEXT_SHOW_PASSWORD_ = 156; + public static final int SEEMP_API_android_provider_Settings__get_USB_MASS_STORAGE_ENABLED_ = 157; + public static final int SEEMP_API_android_provider_Settings__get_VIBRATE_ON_ = 158; + public static final int SEEMP_API_android_provider_Settings__get_HAPTIC_FEEDBACK_ENABLED_ = 159; + public static final int SEEMP_API_android_provider_Settings__get_VOLUME_ALARM_ = 160; + public static final int SEEMP_API_android_provider_Settings__get_VOLUME_BLUETOOTH_SCO_ = 161; + public static final int SEEMP_API_android_provider_Settings__get_VOLUME_MUSIC_ = 162; + public static final int SEEMP_API_android_provider_Settings__get_VOLUME_NOTIFICATION_ = 163; + public static final int SEEMP_API_android_provider_Settings__get_VOLUME_RING_ = 164; + public static final int SEEMP_API_android_provider_Settings__get_VOLUME_SYSTEM_ = 165; + public static final int SEEMP_API_android_provider_Settings__get_VOLUME_VOICE_ = 166; + public static final int SEEMP_API_android_provider_Settings__get_SOUND_EFFECTS_ENABLED_ = 167; + public static final int SEEMP_API_android_provider_Settings__get_MODE_RINGER_STREAMS_AFFECTED_ = 168; + public static final int SEEMP_API_android_provider_Settings__get_MUTE_STREAMS_AFFECTED_ = 169; + public static final int SEEMP_API_android_provider_Settings__get_NOTIFICATION_SOUND_ = 170; + public static final int SEEMP_API_android_provider_Settings__get_APPEND_FOR_LAST_AUDIBLE_ = 171; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_MAX_DHCP_RETRY_COUNT_ = 172; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS_ = 173; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_ = 174; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_ = 175; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_NUM_OPEN_NETWORKS_KEPT_ = 176; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_ON_ = 177; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_SLEEP_POLICY_ = 178; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_SLEEP_POLICY_DEFAULT_ = 179; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_SLEEP_POLICY_NEVER_ = 180; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED_ = 181; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_STATIC_DNS1_ = 182; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_STATIC_DNS2_ = 183; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_STATIC_GATEWAY_ = 184; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_STATIC_IP_ = 185; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_STATIC_NETMASK_ = 186; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_USE_STATIC_IP_ = 187; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE_ = 188; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_WATCHDOG_AP_COUNT_ = 189; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS_ = 190; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED_ = 191; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS_ = 192; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT_ = 193; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_WATCHDOG_MAX_AP_CHECKS_ = 194; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_WATCHDOG_ON_ = 195; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_WATCHDOG_PING_COUNT_ = 196; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_WATCHDOG_PING_DELAY_MS_ = 197; + public static final int SEEMP_API_android_provider_Settings__get_WIFI_WATCHDOG_PING_TIMEOUT_MS_ = 198; + public static final int SEEMP_API_android_provider_Settings__put_ACCELEROMETER_ROTATION_ = 199; + public static final int SEEMP_API_android_provider_Settings__put_USER_ROTATION_ = 200; + public static final int SEEMP_API_android_provider_Settings__put_ADB_ENABLED_ = 201; + public static final int SEEMP_API_android_provider_Settings__put_DEBUG_APP_ = 202; + public static final int SEEMP_API_android_provider_Settings__put_WAIT_FOR_DEBUGGER_ = 203; + public static final int SEEMP_API_android_provider_Settings__put_AIRPLANE_MODE_ON_ = 204; + public static final int SEEMP_API_android_provider_Settings__put_AIRPLANE_MODE_RADIOS_ = 205; + public static final int SEEMP_API_android_provider_Settings__put_ALARM_ALERT_ = 206; + public static final int SEEMP_API_android_provider_Settings__put_NEXT_ALARM_FORMATTED_ = 207; + public static final int SEEMP_API_android_provider_Settings__put_ALWAYS_FINISH_ACTIVITIES_ = 208; + public static final int SEEMP_API_android_provider_Settings__put_ANDROID_ID_ = 209; + public static final int SEEMP_API_android_provider_Settings__put_LOGGING_ID_ = 210; + public static final int SEEMP_API_android_provider_Settings__put_ANIMATOR_DURATION_SCALE_ = 211; + public static final int SEEMP_API_android_provider_Settings__put_WINDOW_ANIMATION_SCALE_ = 212; + public static final int SEEMP_API_android_provider_Settings__put_FONT_SCALE_ = 213; + public static final int SEEMP_API_android_provider_Settings__put_SCREEN_BRIGHTNESS_ = 214; + public static final int SEEMP_API_android_provider_Settings__put_SCREEN_BRIGHTNESS_MODE_ = 215; + public static final int SEEMP_API_android_provider_Settings__put_SCREEN_BRIGHTNESS_MODE_AUTOMATIC_ = 216; + public static final int SEEMP_API_android_provider_Settings__put_SCREEN_BRIGHTNESS_MODE_MANUAL_ = 217; + public static final int SEEMP_API_android_provider_Settings__put_SCREEN_OFF_TIMEOUT_ = 218; + public static final int SEEMP_API_android_provider_Settings__put_DIM_SCREEN_ = 219; + public static final int SEEMP_API_android_provider_Settings__put_TRANSITION_ANIMATION_SCALE_ = 220; + public static final int SEEMP_API_android_provider_Settings__put_STAY_ON_WHILE_PLUGGED_IN_ = 221; + public static final int SEEMP_API_android_provider_Settings__put_WALLPAPER_ACTIVITY_ = 222; + public static final int SEEMP_API_android_provider_Settings__put_SHOW_PROCESSES_ = 223; + public static final int SEEMP_API_android_provider_Settings__put_SHOW_WEB_SUGGESTIONS_ = 224; + public static final int SEEMP_API_android_provider_Settings__put_SHOW_GTALK_SERVICE_STATUS_ = 225; + public static final int SEEMP_API_android_provider_Settings__put_USE_GOOGLE_MAIL_ = 226; + public static final int SEEMP_API_android_provider_Settings__put_AUTO_TIME_ = 227; + public static final int SEEMP_API_android_provider_Settings__put_AUTO_TIME_ZONE_ = 228; + public static final int SEEMP_API_android_provider_Settings__put_DATE_FORMAT_ = 229; + public static final int SEEMP_API_android_provider_Settings__put_TIME_12_24_ = 230; + public static final int SEEMP_API_android_provider_Settings__put_BLUETOOTH_DISCOVERABILITY_ = 231; + public static final int SEEMP_API_android_provider_Settings__put_BLUETOOTH_DISCOVERABILITY_TIMEOUT_ = 232; + public static final int SEEMP_API_android_provider_Settings__put_BLUETOOTH_ON_ = 233; + public static final int SEEMP_API_android_provider_Settings__put_DEVICE_PROVISIONED_ = 234; + public static final int SEEMP_API_android_provider_Settings__put_SETUP_WIZARD_HAS_RUN_ = 235; + public static final int SEEMP_API_android_provider_Settings__put_DTMF_TONE_WHEN_DIALING_ = 236; + public static final int SEEMP_API_android_provider_Settings__put_END_BUTTON_BEHAVIOR_ = 237; + public static final int SEEMP_API_android_provider_Settings__put_RINGTONE_ = 238; + public static final int SEEMP_API_android_provider_Settings__put_MODE_RINGER_ = 239; + public static final int SEEMP_API_android_provider_Settings__put_INSTALL_NON_MARKET_APPS_ = 240; + public static final int SEEMP_API_android_provider_Settings__put_LOCATION_PROVIDERS_ALLOWED_ = 241; + public static final int SEEMP_API_android_provider_Settings__put_LOCK_PATTERN_ENABLED_ = 242; + public static final int SEEMP_API_android_provider_Settings__put_LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED_ = 243; + public static final int SEEMP_API_android_provider_Settings__put_LOCK_PATTERN_VISIBLE_ = 244; + public static final int SEEMP_API_android_provider_Settings__put_NETWORK_PREFERENCE_ = 245; + public static final int SEEMP_API_android_provider_Settings__put_DATA_ROAMING_ = 246; + public static final int SEEMP_API_android_provider_Settings__put_HTTP_PROXY_ = 247; + public static final int SEEMP_API_android_provider_Settings__put_PARENTAL_CONTROL_ENABLED_ = 248; + public static final int SEEMP_API_android_provider_Settings__put_PARENTAL_CONTROL_LAST_UPDATE_ = 249; + public static final int SEEMP_API_android_provider_Settings__put_PARENTAL_CONTROL_REDIRECT_URL_ = 250; + public static final int SEEMP_API_android_provider_Settings__put_RADIO_BLUETOOTH_ = 251; + public static final int SEEMP_API_android_provider_Settings__put_RADIO_CELL_ = 252; + public static final int SEEMP_API_android_provider_Settings__put_RADIO_NFC_ = 253; + public static final int SEEMP_API_android_provider_Settings__put_RADIO_WIFI_ = 254; + public static final int SEEMP_API_android_provider_Settings__put_SYS_PROP_SETTING_VERSION_ = 255; + public static final int SEEMP_API_android_provider_Settings__put_SETTINGS_CLASSNAME_ = 256; + public static final int SEEMP_API_android_provider_Settings__put_TEXT_AUTO_CAPS_ = 257; + public static final int SEEMP_API_android_provider_Settings__put_TEXT_AUTO_PUNCTUATE_ = 258; + public static final int SEEMP_API_android_provider_Settings__put_TEXT_AUTO_REPLACE_ = 259; + public static final int SEEMP_API_android_provider_Settings__put_TEXT_SHOW_PASSWORD_ = 260; + public static final int SEEMP_API_android_provider_Settings__put_USB_MASS_STORAGE_ENABLED_ = 261; + public static final int SEEMP_API_android_provider_Settings__put_VIBRATE_ON_ = 262; + public static final int SEEMP_API_android_provider_Settings__put_HAPTIC_FEEDBACK_ENABLED_ = 263; + public static final int SEEMP_API_android_provider_Settings__put_VOLUME_ALARM_ = 264; + public static final int SEEMP_API_android_provider_Settings__put_VOLUME_BLUETOOTH_SCO_ = 265; + public static final int SEEMP_API_android_provider_Settings__put_VOLUME_MUSIC_ = 266; + public static final int SEEMP_API_android_provider_Settings__put_VOLUME_NOTIFICATION_ = 267; + public static final int SEEMP_API_android_provider_Settings__put_VOLUME_RING_ = 268; + public static final int SEEMP_API_android_provider_Settings__put_VOLUME_SYSTEM_ = 269; + public static final int SEEMP_API_android_provider_Settings__put_VOLUME_VOICE_ = 270; + public static final int SEEMP_API_android_provider_Settings__put_SOUND_EFFECTS_ENABLED_ = 271; + public static final int SEEMP_API_android_provider_Settings__put_MODE_RINGER_STREAMS_AFFECTED_ = 272; + public static final int SEEMP_API_android_provider_Settings__put_MUTE_STREAMS_AFFECTED_ = 273; + public static final int SEEMP_API_android_provider_Settings__put_NOTIFICATION_SOUND_ = 274; + public static final int SEEMP_API_android_provider_Settings__put_APPEND_FOR_LAST_AUDIBLE_ = 275; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_MAX_DHCP_RETRY_COUNT_ = 276; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS_ = 277; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_ = 278; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_ = 279; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_NUM_OPEN_NETWORKS_KEPT_ = 280; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_ON_ = 281; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_SLEEP_POLICY_ = 282; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_SLEEP_POLICY_DEFAULT_ = 283; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_SLEEP_POLICY_NEVER_ = 284; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_SLEEP_POLICY_NEVER_WHILE_PLUGGED_ = 285; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_STATIC_DNS1_ = 286; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_STATIC_DNS2_ = 287; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_STATIC_GATEWAY_ = 288; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_STATIC_IP_ = 289; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_STATIC_NETMASK_ = 290; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_USE_STATIC_IP_ = 291; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE_ = 292; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_WATCHDOG_AP_COUNT_ = 293; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS_ = 294; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED_ = 295; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS_ = 296; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT_ = 297; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_WATCHDOG_MAX_AP_CHECKS_ = 298; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_WATCHDOG_ON_ = 299; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_WATCHDOG_PING_COUNT_ = 300; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_WATCHDOG_PING_DELAY_MS_ = 301; + public static final int SEEMP_API_android_provider_Settings__put_WIFI_WATCHDOG_PING_TIMEOUT_MS_ = 302; + + private final static java.util.Map<String,Integer> value_to_get_map; + static { + value_to_get_map = new java.util.HashMap<String,Integer>( 198 ); + value_to_get_map.put(Settings.System.NOTIFICATION_SOUND, + SEEMP_API_android_provider_Settings__get_NOTIFICATION_SOUND_); + value_to_get_map.put(Settings.System.DTMF_TONE_WHEN_DIALING, + SEEMP_API_android_provider_Settings__get_DTMF_TONE_WHEN_DIALING_); + value_to_get_map.put(Settings.System.LOCK_PATTERN_ENABLED, + SEEMP_API_android_provider_Settings__get_LOCK_PATTERN_ENABLED_); + value_to_get_map.put(Settings.System.WIFI_MAX_DHCP_RETRY_COUNT, + SEEMP_API_android_provider_Settings__get_WIFI_MAX_DHCP_RETRY_COUNT_); + value_to_get_map.put(Settings.System.AUTO_TIME, + SEEMP_API_android_provider_Settings__get_AUTO_TIME_); + value_to_get_map.put(Settings.System.SETUP_WIZARD_HAS_RUN, + SEEMP_API_android_provider_Settings__get_SETUP_WIZARD_HAS_RUN_); + value_to_get_map.put(Settings.System.SYS_PROP_SETTING_VERSION, + SEEMP_API_android_provider_Settings__get_SYS_PROP_SETTING_VERSION_); + value_to_get_map.put(Settings.System.WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS, + SEEMP_API_android_provider_Settings__get_WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS_); + value_to_get_map.put(Settings.System.LOCATION_PROVIDERS_ALLOWED, + SEEMP_API_android_provider_Settings__get_LOCATION_PROVIDERS_ALLOWED_); + value_to_get_map.put(Settings.System.ALARM_ALERT, + SEEMP_API_android_provider_Settings__get_ALARM_ALERT_); + value_to_get_map.put(Settings.System.VIBRATE_ON, + SEEMP_API_android_provider_Settings__get_VIBRATE_ON_); + value_to_get_map.put(Settings.System.USB_MASS_STORAGE_ENABLED, + SEEMP_API_android_provider_Settings__get_USB_MASS_STORAGE_ENABLED_); + value_to_get_map.put(Settings.System.WIFI_WATCHDOG_PING_DELAY_MS, + SEEMP_API_android_provider_Settings__get_WIFI_WATCHDOG_PING_DELAY_MS_); + value_to_get_map.put(Settings.System.FONT_SCALE, + SEEMP_API_android_provider_Settings__get_FONT_SCALE_); + value_to_get_map.put(Settings.System.WIFI_WATCHDOG_AP_COUNT, + SEEMP_API_android_provider_Settings__get_WIFI_WATCHDOG_AP_COUNT_); + value_to_get_map.put(Settings.System.ALWAYS_FINISH_ACTIVITIES, + SEEMP_API_android_provider_Settings__get_ALWAYS_FINISH_ACTIVITIES_); + value_to_get_map.put(Settings.System.ACCELEROMETER_ROTATION, + SEEMP_API_android_provider_Settings__get_ACCELEROMETER_ROTATION_); + value_to_get_map.put(Settings.System.WIFI_WATCHDOG_PING_TIMEOUT_MS, + SEEMP_API_android_provider_Settings__get_WIFI_WATCHDOG_PING_TIMEOUT_MS_); + value_to_get_map.put(Settings.System.VOLUME_NOTIFICATION, + SEEMP_API_android_provider_Settings__get_VOLUME_NOTIFICATION_); + value_to_get_map.put(Settings.System.AIRPLANE_MODE_ON, + SEEMP_API_android_provider_Settings__get_AIRPLANE_MODE_ON_); + value_to_get_map.put(Settings.System.WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS, + SEEMP_API_android_provider_Settings__get_WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS_); + value_to_get_map.put(Settings.System.WIFI_STATIC_IP, + SEEMP_API_android_provider_Settings__get_WIFI_STATIC_IP_); + value_to_get_map.put(Settings.System.RADIO_BLUETOOTH, + SEEMP_API_android_provider_Settings__get_RADIO_BLUETOOTH_); + value_to_get_map.put(Settings.System.BLUETOOTH_DISCOVERABILITY_TIMEOUT, + SEEMP_API_android_provider_Settings__get_BLUETOOTH_DISCOVERABILITY_TIMEOUT_); + value_to_get_map.put(Settings.System.VOLUME_RING, + SEEMP_API_android_provider_Settings__get_VOLUME_RING_); + value_to_get_map.put(Settings.System.MODE_RINGER_STREAMS_AFFECTED, + SEEMP_API_android_provider_Settings__get_MODE_RINGER_STREAMS_AFFECTED_); + value_to_get_map.put(Settings.System.VOLUME_SYSTEM, + SEEMP_API_android_provider_Settings__get_VOLUME_SYSTEM_); + value_to_get_map.put(Settings.System.SCREEN_OFF_TIMEOUT, + SEEMP_API_android_provider_Settings__get_SCREEN_OFF_TIMEOUT_); + value_to_get_map.put(Settings.System.RADIO_WIFI, + SEEMP_API_android_provider_Settings__get_RADIO_WIFI_); + value_to_get_map.put(Settings.System.AUTO_TIME_ZONE, + SEEMP_API_android_provider_Settings__get_AUTO_TIME_ZONE_); + value_to_get_map.put(Settings.System.TEXT_AUTO_CAPS, + SEEMP_API_android_provider_Settings__get_TEXT_AUTO_CAPS_); + value_to_get_map.put(Settings.System.WALLPAPER_ACTIVITY, + SEEMP_API_android_provider_Settings__get_WALLPAPER_ACTIVITY_); + value_to_get_map.put(Settings.System.ANIMATOR_DURATION_SCALE, + SEEMP_API_android_provider_Settings__get_ANIMATOR_DURATION_SCALE_); + value_to_get_map.put(Settings.System.WIFI_NUM_OPEN_NETWORKS_KEPT, + SEEMP_API_android_provider_Settings__get_WIFI_NUM_OPEN_NETWORKS_KEPT_); + value_to_get_map.put(Settings.System.LOCK_PATTERN_VISIBLE, + SEEMP_API_android_provider_Settings__get_LOCK_PATTERN_VISIBLE_); + value_to_get_map.put(Settings.System.VOLUME_VOICE, + SEEMP_API_android_provider_Settings__get_VOLUME_VOICE_); + value_to_get_map.put(Settings.System.DEBUG_APP, + SEEMP_API_android_provider_Settings__get_DEBUG_APP_); + value_to_get_map.put(Settings.System.WIFI_ON, + SEEMP_API_android_provider_Settings__get_WIFI_ON_); + value_to_get_map.put(Settings.System.TEXT_SHOW_PASSWORD, + SEEMP_API_android_provider_Settings__get_TEXT_SHOW_PASSWORD_); + value_to_get_map.put(Settings.System.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, + SEEMP_API_android_provider_Settings__get_WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_); + value_to_get_map.put(Settings.System.WIFI_SLEEP_POLICY, + SEEMP_API_android_provider_Settings__get_WIFI_SLEEP_POLICY_); + value_to_get_map.put(Settings.System.VOLUME_MUSIC, + SEEMP_API_android_provider_Settings__get_VOLUME_MUSIC_); + value_to_get_map.put(Settings.System.PARENTAL_CONTROL_LAST_UPDATE, + SEEMP_API_android_provider_Settings__get_PARENTAL_CONTROL_LAST_UPDATE_); + value_to_get_map.put(Settings.System.DEVICE_PROVISIONED, + SEEMP_API_android_provider_Settings__get_DEVICE_PROVISIONED_); + value_to_get_map.put(Settings.System.HTTP_PROXY, + SEEMP_API_android_provider_Settings__get_HTTP_PROXY_); + value_to_get_map.put(Settings.System.ANDROID_ID, + SEEMP_API_android_provider_Settings__get_ANDROID_ID_); + value_to_get_map.put(Settings.System.WIFI_WATCHDOG_MAX_AP_CHECKS, + SEEMP_API_android_provider_Settings__get_WIFI_WATCHDOG_MAX_AP_CHECKS_); + value_to_get_map.put(Settings.System.END_BUTTON_BEHAVIOR, + SEEMP_API_android_provider_Settings__get_END_BUTTON_BEHAVIOR_); + value_to_get_map.put(Settings.System.NEXT_ALARM_FORMATTED, + SEEMP_API_android_provider_Settings__get_NEXT_ALARM_FORMATTED_); + value_to_get_map.put(Settings.System.RADIO_CELL, + SEEMP_API_android_provider_Settings__get_RADIO_CELL_); + value_to_get_map.put(Settings.System.PARENTAL_CONTROL_ENABLED, + SEEMP_API_android_provider_Settings__get_PARENTAL_CONTROL_ENABLED_); + value_to_get_map.put(Settings.System.BLUETOOTH_ON, + SEEMP_API_android_provider_Settings__get_BLUETOOTH_ON_); + value_to_get_map.put(Settings.System.WINDOW_ANIMATION_SCALE, + SEEMP_API_android_provider_Settings__get_WINDOW_ANIMATION_SCALE_); + value_to_get_map.put(Settings.System.WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED, + SEEMP_API_android_provider_Settings__get_WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED_); + value_to_get_map.put(Settings.System.BLUETOOTH_DISCOVERABILITY, + SEEMP_API_android_provider_Settings__get_BLUETOOTH_DISCOVERABILITY_); + value_to_get_map.put(Settings.System.WIFI_STATIC_DNS1, + SEEMP_API_android_provider_Settings__get_WIFI_STATIC_DNS1_); + value_to_get_map.put(Settings.System.WIFI_STATIC_DNS2, + SEEMP_API_android_provider_Settings__get_WIFI_STATIC_DNS2_); + value_to_get_map.put(Settings.System.HAPTIC_FEEDBACK_ENABLED, + SEEMP_API_android_provider_Settings__get_HAPTIC_FEEDBACK_ENABLED_); + value_to_get_map.put(Settings.System.SHOW_WEB_SUGGESTIONS, + SEEMP_API_android_provider_Settings__get_SHOW_WEB_SUGGESTIONS_); + value_to_get_map.put(Settings.System.PARENTAL_CONTROL_REDIRECT_URL, + SEEMP_API_android_provider_Settings__get_PARENTAL_CONTROL_REDIRECT_URL_); + value_to_get_map.put(Settings.System.DATE_FORMAT, + SEEMP_API_android_provider_Settings__get_DATE_FORMAT_); + value_to_get_map.put(Settings.System.RADIO_NFC, + SEEMP_API_android_provider_Settings__get_RADIO_NFC_); + value_to_get_map.put(Settings.System.AIRPLANE_MODE_RADIOS, + SEEMP_API_android_provider_Settings__get_AIRPLANE_MODE_RADIOS_); + value_to_get_map.put(Settings.System.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED, + SEEMP_API_android_provider_Settings__get_LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED_); + value_to_get_map.put(Settings.System.TIME_12_24, + SEEMP_API_android_provider_Settings__get_TIME_12_24_); + value_to_get_map.put(Settings.System.WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT, + SEEMP_API_android_provider_Settings__get_WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT_); + value_to_get_map.put(Settings.System.VOLUME_BLUETOOTH_SCO, + SEEMP_API_android_provider_Settings__get_VOLUME_BLUETOOTH_SCO_); + value_to_get_map.put(Settings.System.USER_ROTATION, + SEEMP_API_android_provider_Settings__get_USER_ROTATION_); + value_to_get_map.put(Settings.System.WIFI_STATIC_GATEWAY, + SEEMP_API_android_provider_Settings__get_WIFI_STATIC_GATEWAY_); + value_to_get_map.put(Settings.System.STAY_ON_WHILE_PLUGGED_IN, + SEEMP_API_android_provider_Settings__get_STAY_ON_WHILE_PLUGGED_IN_); + value_to_get_map.put(Settings.System.SOUND_EFFECTS_ENABLED, + SEEMP_API_android_provider_Settings__get_SOUND_EFFECTS_ENABLED_); + value_to_get_map.put(Settings.System.WIFI_WATCHDOG_PING_COUNT, + SEEMP_API_android_provider_Settings__get_WIFI_WATCHDOG_PING_COUNT_); + value_to_get_map.put(Settings.System.DATA_ROAMING, + SEEMP_API_android_provider_Settings__get_DATA_ROAMING_); + value_to_get_map.put(Settings.System.SETTINGS_CLASSNAME, + SEEMP_API_android_provider_Settings__get_SETTINGS_CLASSNAME_); + value_to_get_map.put(Settings.System.TRANSITION_ANIMATION_SCALE, + SEEMP_API_android_provider_Settings__get_TRANSITION_ANIMATION_SCALE_); + value_to_get_map.put(Settings.System.WAIT_FOR_DEBUGGER, + SEEMP_API_android_provider_Settings__get_WAIT_FOR_DEBUGGER_); + value_to_get_map.put(Settings.System.INSTALL_NON_MARKET_APPS, + SEEMP_API_android_provider_Settings__get_INSTALL_NON_MARKET_APPS_); + value_to_get_map.put(Settings.System.ADB_ENABLED, + SEEMP_API_android_provider_Settings__get_ADB_ENABLED_); + value_to_get_map.put(Settings.System.WIFI_USE_STATIC_IP, + SEEMP_API_android_provider_Settings__get_WIFI_USE_STATIC_IP_); + value_to_get_map.put(Settings.System.DIM_SCREEN, + SEEMP_API_android_provider_Settings__get_DIM_SCREEN_); + value_to_get_map.put(Settings.System.VOLUME_ALARM, + SEEMP_API_android_provider_Settings__get_VOLUME_ALARM_); + value_to_get_map.put(Settings.System.WIFI_WATCHDOG_ON, + SEEMP_API_android_provider_Settings__get_WIFI_WATCHDOG_ON_); + value_to_get_map.put(Settings.System.WIFI_STATIC_NETMASK, + SEEMP_API_android_provider_Settings__get_WIFI_STATIC_NETMASK_); + value_to_get_map.put(Settings.System.NETWORK_PREFERENCE, + SEEMP_API_android_provider_Settings__get_NETWORK_PREFERENCE_); + value_to_get_map.put(Settings.System.SHOW_PROCESSES, + SEEMP_API_android_provider_Settings__get_SHOW_PROCESSES_); + value_to_get_map.put(Settings.System.TEXT_AUTO_REPLACE, + SEEMP_API_android_provider_Settings__get_TEXT_AUTO_REPLACE_); + value_to_get_map.put(Settings.System.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, + SEEMP_API_android_provider_Settings__get_WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_); + value_to_get_map.put(Settings.System.APPEND_FOR_LAST_AUDIBLE, + SEEMP_API_android_provider_Settings__get_APPEND_FOR_LAST_AUDIBLE_); + value_to_get_map.put(Settings.System.SHOW_GTALK_SERVICE_STATUS, + SEEMP_API_android_provider_Settings__get_SHOW_GTALK_SERVICE_STATUS_); + value_to_get_map.put(Settings.System.SCREEN_BRIGHTNESS, + SEEMP_API_android_provider_Settings__get_SCREEN_BRIGHTNESS_); + value_to_get_map.put(Settings.System.USE_GOOGLE_MAIL, + SEEMP_API_android_provider_Settings__get_USE_GOOGLE_MAIL_); + value_to_get_map.put(Settings.System.RINGTONE, + SEEMP_API_android_provider_Settings__get_RINGTONE_); + value_to_get_map.put(Settings.System.LOGGING_ID, + SEEMP_API_android_provider_Settings__get_LOGGING_ID_); + value_to_get_map.put(Settings.System.MODE_RINGER, + SEEMP_API_android_provider_Settings__get_MODE_RINGER_); + value_to_get_map.put(Settings.System.MUTE_STREAMS_AFFECTED, + SEEMP_API_android_provider_Settings__get_MUTE_STREAMS_AFFECTED_); + value_to_get_map.put(Settings.System.WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE, + SEEMP_API_android_provider_Settings__get_WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE_); + value_to_get_map.put(Settings.System.TEXT_AUTO_PUNCTUATE, + SEEMP_API_android_provider_Settings__get_TEXT_AUTO_PUNCTUATE_); + value_to_get_map.put(Settings.System.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS, + SEEMP_API_android_provider_Settings__get_WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS_); + value_to_get_map.put(Settings.System.SCREEN_BRIGHTNESS_MODE, + SEEMP_API_android_provider_Settings__get_SCREEN_BRIGHTNESS_MODE_); + } + + public static int getSeempGetApiIdFromValue( String v ) + { + Integer result = value_to_get_map.get( v ); + if (result == null) + { + result = -1; + } + return result; + } + + private final static java.util.Map<String,Integer> value_to_put_map; + static { + value_to_put_map = new java.util.HashMap<String,Integer>( 198 ); + value_to_put_map.put(Settings.System.NOTIFICATION_SOUND, + SEEMP_API_android_provider_Settings__put_NOTIFICATION_SOUND_); + value_to_put_map.put(Settings.System.DTMF_TONE_WHEN_DIALING, + SEEMP_API_android_provider_Settings__put_DTMF_TONE_WHEN_DIALING_); + value_to_put_map.put(Settings.System.LOCK_PATTERN_ENABLED, + SEEMP_API_android_provider_Settings__put_LOCK_PATTERN_ENABLED_); + value_to_put_map.put(Settings.System.WIFI_MAX_DHCP_RETRY_COUNT, + SEEMP_API_android_provider_Settings__put_WIFI_MAX_DHCP_RETRY_COUNT_); + value_to_put_map.put(Settings.System.AUTO_TIME, + SEEMP_API_android_provider_Settings__put_AUTO_TIME_); + value_to_put_map.put(Settings.System.SETUP_WIZARD_HAS_RUN, + SEEMP_API_android_provider_Settings__put_SETUP_WIZARD_HAS_RUN_); + value_to_put_map.put(Settings.System.SYS_PROP_SETTING_VERSION, + SEEMP_API_android_provider_Settings__put_SYS_PROP_SETTING_VERSION_); + value_to_put_map.put(Settings.System.WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS, + SEEMP_API_android_provider_Settings__put_WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS_); + value_to_put_map.put(Settings.System.LOCATION_PROVIDERS_ALLOWED, + SEEMP_API_android_provider_Settings__put_LOCATION_PROVIDERS_ALLOWED_); + value_to_put_map.put(Settings.System.ALARM_ALERT, + SEEMP_API_android_provider_Settings__put_ALARM_ALERT_); + value_to_put_map.put(Settings.System.VIBRATE_ON, + SEEMP_API_android_provider_Settings__put_VIBRATE_ON_); + value_to_put_map.put(Settings.System.USB_MASS_STORAGE_ENABLED, + SEEMP_API_android_provider_Settings__put_USB_MASS_STORAGE_ENABLED_); + value_to_put_map.put(Settings.System.WIFI_WATCHDOG_PING_DELAY_MS, + SEEMP_API_android_provider_Settings__put_WIFI_WATCHDOG_PING_DELAY_MS_); + value_to_put_map.put(Settings.System.FONT_SCALE, + SEEMP_API_android_provider_Settings__put_FONT_SCALE_); + value_to_put_map.put(Settings.System.WIFI_WATCHDOG_AP_COUNT, + SEEMP_API_android_provider_Settings__put_WIFI_WATCHDOG_AP_COUNT_); + value_to_put_map.put(Settings.System.ALWAYS_FINISH_ACTIVITIES, + SEEMP_API_android_provider_Settings__put_ALWAYS_FINISH_ACTIVITIES_); + value_to_put_map.put(Settings.System.ACCELEROMETER_ROTATION, + SEEMP_API_android_provider_Settings__put_ACCELEROMETER_ROTATION_); + value_to_put_map.put(Settings.System.WIFI_WATCHDOG_PING_TIMEOUT_MS, + SEEMP_API_android_provider_Settings__put_WIFI_WATCHDOG_PING_TIMEOUT_MS_); + value_to_put_map.put(Settings.System.VOLUME_NOTIFICATION, + SEEMP_API_android_provider_Settings__put_VOLUME_NOTIFICATION_); + value_to_put_map.put(Settings.System.AIRPLANE_MODE_ON, + SEEMP_API_android_provider_Settings__put_AIRPLANE_MODE_ON_); + value_to_put_map.put(Settings.System.WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS, + SEEMP_API_android_provider_Settings__put_WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS_); + value_to_put_map.put(Settings.System.WIFI_STATIC_IP, + SEEMP_API_android_provider_Settings__put_WIFI_STATIC_IP_); + value_to_put_map.put(Settings.System.RADIO_BLUETOOTH, + SEEMP_API_android_provider_Settings__put_RADIO_BLUETOOTH_); + value_to_put_map.put(Settings.System.BLUETOOTH_DISCOVERABILITY_TIMEOUT, + SEEMP_API_android_provider_Settings__put_BLUETOOTH_DISCOVERABILITY_TIMEOUT_); + value_to_put_map.put(Settings.System.VOLUME_RING, + SEEMP_API_android_provider_Settings__put_VOLUME_RING_); + value_to_put_map.put(Settings.System.MODE_RINGER_STREAMS_AFFECTED, + SEEMP_API_android_provider_Settings__put_MODE_RINGER_STREAMS_AFFECTED_); + value_to_put_map.put(Settings.System.VOLUME_SYSTEM, + SEEMP_API_android_provider_Settings__put_VOLUME_SYSTEM_); + value_to_put_map.put(Settings.System.SCREEN_OFF_TIMEOUT, + SEEMP_API_android_provider_Settings__put_SCREEN_OFF_TIMEOUT_); + value_to_put_map.put(Settings.System.RADIO_WIFI, + SEEMP_API_android_provider_Settings__put_RADIO_WIFI_); + value_to_put_map.put(Settings.System.AUTO_TIME_ZONE, + SEEMP_API_android_provider_Settings__put_AUTO_TIME_ZONE_); + value_to_put_map.put(Settings.System.TEXT_AUTO_CAPS, + SEEMP_API_android_provider_Settings__put_TEXT_AUTO_CAPS_); + value_to_put_map.put(Settings.System.WALLPAPER_ACTIVITY, + SEEMP_API_android_provider_Settings__put_WALLPAPER_ACTIVITY_); + value_to_put_map.put(Settings.System.ANIMATOR_DURATION_SCALE, + SEEMP_API_android_provider_Settings__put_ANIMATOR_DURATION_SCALE_); + value_to_put_map.put(Settings.System.WIFI_NUM_OPEN_NETWORKS_KEPT, + SEEMP_API_android_provider_Settings__put_WIFI_NUM_OPEN_NETWORKS_KEPT_); + value_to_put_map.put(Settings.System.LOCK_PATTERN_VISIBLE, + SEEMP_API_android_provider_Settings__put_LOCK_PATTERN_VISIBLE_); + value_to_put_map.put(Settings.System.VOLUME_VOICE, + SEEMP_API_android_provider_Settings__put_VOLUME_VOICE_); + value_to_put_map.put(Settings.System.DEBUG_APP, + SEEMP_API_android_provider_Settings__put_DEBUG_APP_); + value_to_put_map.put(Settings.System.WIFI_ON, + SEEMP_API_android_provider_Settings__put_WIFI_ON_); + value_to_put_map.put(Settings.System.TEXT_SHOW_PASSWORD, + SEEMP_API_android_provider_Settings__put_TEXT_SHOW_PASSWORD_); + value_to_put_map.put(Settings.System.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, + SEEMP_API_android_provider_Settings__put_WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_); + value_to_put_map.put(Settings.System.WIFI_SLEEP_POLICY, + SEEMP_API_android_provider_Settings__put_WIFI_SLEEP_POLICY_); + value_to_put_map.put(Settings.System.VOLUME_MUSIC, + SEEMP_API_android_provider_Settings__put_VOLUME_MUSIC_); + value_to_put_map.put(Settings.System.PARENTAL_CONTROL_LAST_UPDATE, + SEEMP_API_android_provider_Settings__put_PARENTAL_CONTROL_LAST_UPDATE_); + value_to_put_map.put(Settings.System.DEVICE_PROVISIONED, + SEEMP_API_android_provider_Settings__put_DEVICE_PROVISIONED_); + value_to_put_map.put(Settings.System.HTTP_PROXY, + SEEMP_API_android_provider_Settings__put_HTTP_PROXY_); + value_to_put_map.put(Settings.System.ANDROID_ID, + SEEMP_API_android_provider_Settings__put_ANDROID_ID_); + value_to_put_map.put(Settings.System.WIFI_WATCHDOG_MAX_AP_CHECKS, + SEEMP_API_android_provider_Settings__put_WIFI_WATCHDOG_MAX_AP_CHECKS_); + value_to_put_map.put(Settings.System.END_BUTTON_BEHAVIOR, + SEEMP_API_android_provider_Settings__put_END_BUTTON_BEHAVIOR_); + value_to_put_map.put(Settings.System.NEXT_ALARM_FORMATTED, + SEEMP_API_android_provider_Settings__put_NEXT_ALARM_FORMATTED_); + value_to_put_map.put(Settings.System.RADIO_CELL, + SEEMP_API_android_provider_Settings__put_RADIO_CELL_); + value_to_put_map.put(Settings.System.PARENTAL_CONTROL_ENABLED, + SEEMP_API_android_provider_Settings__put_PARENTAL_CONTROL_ENABLED_); + value_to_put_map.put(Settings.System.BLUETOOTH_ON, + SEEMP_API_android_provider_Settings__put_BLUETOOTH_ON_); + value_to_put_map.put(Settings.System.WINDOW_ANIMATION_SCALE, + SEEMP_API_android_provider_Settings__put_WINDOW_ANIMATION_SCALE_); + value_to_put_map.put(Settings.System.WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED, + SEEMP_API_android_provider_Settings__put_WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED_); + value_to_put_map.put(Settings.System.BLUETOOTH_DISCOVERABILITY, + SEEMP_API_android_provider_Settings__put_BLUETOOTH_DISCOVERABILITY_); + value_to_put_map.put(Settings.System.WIFI_STATIC_DNS1, + SEEMP_API_android_provider_Settings__put_WIFI_STATIC_DNS1_); + value_to_put_map.put(Settings.System.WIFI_STATIC_DNS2, + SEEMP_API_android_provider_Settings__put_WIFI_STATIC_DNS2_); + value_to_put_map.put(Settings.System.HAPTIC_FEEDBACK_ENABLED, + SEEMP_API_android_provider_Settings__put_HAPTIC_FEEDBACK_ENABLED_); + value_to_put_map.put(Settings.System.SHOW_WEB_SUGGESTIONS, + SEEMP_API_android_provider_Settings__put_SHOW_WEB_SUGGESTIONS_); + value_to_put_map.put(Settings.System.PARENTAL_CONTROL_REDIRECT_URL, + SEEMP_API_android_provider_Settings__put_PARENTAL_CONTROL_REDIRECT_URL_); + value_to_put_map.put(Settings.System.DATE_FORMAT, + SEEMP_API_android_provider_Settings__put_DATE_FORMAT_); + value_to_put_map.put(Settings.System.RADIO_NFC, + SEEMP_API_android_provider_Settings__put_RADIO_NFC_); + value_to_put_map.put(Settings.System.AIRPLANE_MODE_RADIOS, + SEEMP_API_android_provider_Settings__put_AIRPLANE_MODE_RADIOS_); + value_to_put_map.put(Settings.System.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED, + SEEMP_API_android_provider_Settings__put_LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED_); + value_to_put_map.put(Settings.System.TIME_12_24, + SEEMP_API_android_provider_Settings__put_TIME_12_24_); + value_to_put_map.put(Settings.System.WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT, + SEEMP_API_android_provider_Settings__put_WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT_); + value_to_put_map.put(Settings.System.VOLUME_BLUETOOTH_SCO, + SEEMP_API_android_provider_Settings__put_VOLUME_BLUETOOTH_SCO_); + value_to_put_map.put(Settings.System.USER_ROTATION, + SEEMP_API_android_provider_Settings__put_USER_ROTATION_); + value_to_put_map.put(Settings.System.WIFI_STATIC_GATEWAY, + SEEMP_API_android_provider_Settings__put_WIFI_STATIC_GATEWAY_); + value_to_put_map.put(Settings.System.STAY_ON_WHILE_PLUGGED_IN, + SEEMP_API_android_provider_Settings__put_STAY_ON_WHILE_PLUGGED_IN_); + value_to_put_map.put(Settings.System.SOUND_EFFECTS_ENABLED, + SEEMP_API_android_provider_Settings__put_SOUND_EFFECTS_ENABLED_); + value_to_put_map.put(Settings.System.WIFI_WATCHDOG_PING_COUNT, + SEEMP_API_android_provider_Settings__put_WIFI_WATCHDOG_PING_COUNT_); + value_to_put_map.put(Settings.System.DATA_ROAMING, + SEEMP_API_android_provider_Settings__put_DATA_ROAMING_); + value_to_put_map.put(Settings.System.SETTINGS_CLASSNAME, + SEEMP_API_android_provider_Settings__put_SETTINGS_CLASSNAME_); + value_to_put_map.put(Settings.System.TRANSITION_ANIMATION_SCALE, + SEEMP_API_android_provider_Settings__put_TRANSITION_ANIMATION_SCALE_); + value_to_put_map.put(Settings.System.WAIT_FOR_DEBUGGER, + SEEMP_API_android_provider_Settings__put_WAIT_FOR_DEBUGGER_); + value_to_put_map.put(Settings.System.INSTALL_NON_MARKET_APPS, + SEEMP_API_android_provider_Settings__put_INSTALL_NON_MARKET_APPS_); + value_to_put_map.put(Settings.System.ADB_ENABLED, + SEEMP_API_android_provider_Settings__put_ADB_ENABLED_); + value_to_put_map.put(Settings.System.WIFI_USE_STATIC_IP, + SEEMP_API_android_provider_Settings__put_WIFI_USE_STATIC_IP_); + value_to_put_map.put(Settings.System.DIM_SCREEN, + SEEMP_API_android_provider_Settings__put_DIM_SCREEN_); + value_to_put_map.put(Settings.System.VOLUME_ALARM, + SEEMP_API_android_provider_Settings__put_VOLUME_ALARM_); + value_to_put_map.put(Settings.System.WIFI_WATCHDOG_ON, + SEEMP_API_android_provider_Settings__put_WIFI_WATCHDOG_ON_); + value_to_put_map.put(Settings.System.WIFI_STATIC_NETMASK, + SEEMP_API_android_provider_Settings__put_WIFI_STATIC_NETMASK_); + value_to_put_map.put(Settings.System.NETWORK_PREFERENCE, + SEEMP_API_android_provider_Settings__put_NETWORK_PREFERENCE_); + value_to_put_map.put(Settings.System.SHOW_PROCESSES, + SEEMP_API_android_provider_Settings__put_SHOW_PROCESSES_); + value_to_put_map.put(Settings.System.TEXT_AUTO_REPLACE, + SEEMP_API_android_provider_Settings__put_TEXT_AUTO_REPLACE_); + value_to_put_map.put(Settings.System.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, + SEEMP_API_android_provider_Settings__put_WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_); + value_to_put_map.put(Settings.System.APPEND_FOR_LAST_AUDIBLE, + SEEMP_API_android_provider_Settings__put_APPEND_FOR_LAST_AUDIBLE_); + value_to_put_map.put(Settings.System.SHOW_GTALK_SERVICE_STATUS, + SEEMP_API_android_provider_Settings__put_SHOW_GTALK_SERVICE_STATUS_); + value_to_put_map.put(Settings.System.SCREEN_BRIGHTNESS, + SEEMP_API_android_provider_Settings__put_SCREEN_BRIGHTNESS_); + value_to_put_map.put(Settings.System.USE_GOOGLE_MAIL, + SEEMP_API_android_provider_Settings__put_USE_GOOGLE_MAIL_); + value_to_put_map.put(Settings.System.RINGTONE, + SEEMP_API_android_provider_Settings__put_RINGTONE_); + value_to_put_map.put(Settings.System.LOGGING_ID, + SEEMP_API_android_provider_Settings__put_LOGGING_ID_); + value_to_put_map.put(Settings.System.MODE_RINGER, + SEEMP_API_android_provider_Settings__put_MODE_RINGER_); + value_to_put_map.put(Settings.System.MUTE_STREAMS_AFFECTED, + SEEMP_API_android_provider_Settings__put_MUTE_STREAMS_AFFECTED_); + value_to_put_map.put(Settings.System.WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE, + SEEMP_API_android_provider_Settings__put_WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE_); + value_to_put_map.put(Settings.System.TEXT_AUTO_PUNCTUATE, + SEEMP_API_android_provider_Settings__put_TEXT_AUTO_PUNCTUATE_); + value_to_put_map.put(Settings.System.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS, + SEEMP_API_android_provider_Settings__put_WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS_); + value_to_put_map.put(Settings.System.SCREEN_BRIGHTNESS_MODE, + SEEMP_API_android_provider_Settings__put_SCREEN_BRIGHTNESS_MODE_); + } + + public static int getSeempPutApiIdFromValue( String v ) + { + Integer result = value_to_put_map.get( v ); + if (result == null) + { + result = -1; + } + return result; + } +} diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 1269ad9..7b2d299 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -778,6 +778,9 @@ public final class Display { synchronized (this) { updateDisplayInfoLocked(); mDisplayInfo.getAppMetrics(outMetrics, mDisplayAdjustments); + if (getDisplayId() == DEFAULT_DISPLAY) { + outMetrics.densityDpi = DisplayMetrics.DENSITY_DEVICE_DEFAULT; + } } } diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index ee76274..85321f9 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -564,6 +564,11 @@ public final class DisplayInfo implements Parcelable { if (!compatInfo.equals(CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO)) { compatInfo.applyToDisplayMetrics(outMetrics); + } else if (type == Display.TYPE_BUILT_IN + && (flags & Display.FLAG_PRESENTATION) == 0) { + outMetrics.setDensity(DisplayMetrics.DENSITY_PREFERRED); + } else { + outMetrics.setDensity(logicalDensityDpi); } } diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 5994d4f..b3f4046 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -130,7 +130,7 @@ interface IWindowManager boolean inKeyguardRestrictedInputMode(); void dismissKeyguard(); void keyguardGoingAway(boolean disableWindowAnimations, - boolean keyguardGoingToNotificationShade); + boolean keyguardGoingToNotificationShade, boolean keyguardShowingMedia); void closeSystemDialogs(String reason); @@ -232,6 +232,16 @@ interface IWindowManager Bitmap screenshotApplications(IBinder appToken, int displayId, int maxWidth, int maxHeight); /** + * Get the current x offset for the wallpaper + */ + int getLastWallpaperX(); + + /** + * Get the current y offset for the wallpaper + */ + int getLastWallpaperY(); + + /** * Called by the status bar to notify Views of changes to System UI visiblity. */ oneway void statusBarVisibilityChanged(int visibility); @@ -240,6 +250,12 @@ interface IWindowManager * Device has a software navigation bar (separate from the status bar). */ boolean hasNavigationBar(); + boolean hasPermanentMenuKey(); + + /** + * Device needs a software navigation bar (because it has no hardware keys). + */ + boolean needsNavigationBar(); /** * Lock the device immediately with the specified options (can be null). @@ -271,4 +287,6 @@ interface IWindowManager * @return The frame statistics or null if the window does not exist. */ WindowContentFrameStats getWindowContentFrameStats(IBinder token); + + void setLiveLockscreenEdgeDetector(boolean enable); } diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 73b4a6e..3956237 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -185,6 +185,16 @@ interface IWindowSession { */ void setWallpaperDisplayOffset(IBinder windowToken, int x, int y); + /** + * Get the current x offset for the wallpaper + */ + int getLastWallpaperX(); + + /** + * Get the current y offset for the wallpaper + */ + int getLastWallpaperY(); + Bundle sendWallpaperCommand(IBinder window, String action, int x, int y, int z, in Bundle extras, boolean sync); diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index cc4598d..5f88c11 100644 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -256,6 +256,15 @@ public final class InputDevice implements Parcelable { public static final int SOURCE_TOUCH_NAVIGATION = 0x00200000 | SOURCE_CLASS_NONE; /** + * The input source is a rotating encoder device whose motions should be interpreted as akin to + * those of a scroll wheel. + * + * @see #SOURCE_CLASS_NONE + * {@hide} + */ + public static final int SOURCE_ROTARY_ENCODER = 0x00400000 | SOURCE_CLASS_NONE; + + /** * The input source is a joystick. * (It may also be a {@link #SOURCE_GAMEPAD}). * @@ -272,6 +281,18 @@ public final class InputDevice implements Parcelable { public static final int SOURCE_HDMI = 0x02000000 | SOURCE_CLASS_BUTTON; /** + * The input source is a touch device whose motions should be interpreted as gestures. + * + * For example, an upward swipe should be treated the same as a swipe of the touchscreen. + * The same should apply for left, right, down swipes. Complex gestures may also be input. + * + * @see #SOURCE_CLASS_NONE + * + * @hide + */ + public static final int SOURCE_GESTURE_SENSOR = 0x10000000 | SOURCE_CLASS_NONE; + + /** * A special input source constant that is used when filtering input devices * to match devices that provide any type of input source. */ @@ -948,6 +969,7 @@ public final class InputDevice implements Parcelable { appendSourceDescriptionIfApplicable(description, SOURCE_TOUCHPAD, "touchpad"); appendSourceDescriptionIfApplicable(description, SOURCE_JOYSTICK, "joystick"); appendSourceDescriptionIfApplicable(description, SOURCE_GAMEPAD, "gamepad"); + appendSourceDescriptionIfApplicable(description, SOURCE_GESTURE_SENSOR, "gesture"); description.append(" )\n"); final int numAxes = mMotionRanges.size(); diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index a9bf92b..d128288 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -761,6 +761,19 @@ public class KeyEvent extends InputEvent implements Parcelable { * Backs out one level of a navigation hierarchy or collapses the item that currently has * focus. */ public static final int KEYCODE_NAVIGATE_OUT = 263; + /** Key code constant: Primary stem key for Wear + * Main power/reset button on watch. + * @hide */ + public static final int KEYCODE_STEM_PRIMARY = 264; + /** Key code constant: Generic stem key 1 for Wear + * @hide */ + public static final int KEYCODE_STEM_1 = 265; + /** Key code constant: Generic stem key 2 for Wear + * @hide */ + public static final int KEYCODE_STEM_2 = 266; + /** Key code constant: Generic stem key 3 for Wear + * @hide */ + public static final int KEYCODE_STEM_3 = 267; /** Key code constant: Skip forward media key. */ public static final int KEYCODE_MEDIA_SKIP_FORWARD = 272; /** Key code constant: Skip backward media key. */ @@ -771,8 +784,11 @@ public class KeyEvent extends InputEvent implements Parcelable { /** Key code constant: Step backward media key. * Steps media backward, one frame at a time. */ public static final int KEYCODE_MEDIA_STEP_BACKWARD = 275; + /** Key code constant: put device to sleep unless a wakelock is held. + * @hide */ + public static final int KEYCODE_SOFT_SLEEP = 276; - private static final int LAST_KEYCODE = KEYCODE_MEDIA_STEP_BACKWARD; + private static final int LAST_KEYCODE = KEYCODE_SOFT_SLEEP; // NOTE: If you add a new keycode here you must also add it to: // isSystem() @@ -1828,8 +1844,17 @@ public class KeyEvent extends InputEvent implements Parcelable { switch (keyCode) { case KeyEvent.KEYCODE_BACK: case KeyEvent.KEYCODE_MENU: + case KeyEvent.KEYCODE_HOME: case KeyEvent.KEYCODE_WAKEUP: case KeyEvent.KEYCODE_PAIRING: + case KeyEvent.KEYCODE_VOLUME_UP: + case KeyEvent.KEYCODE_VOLUME_DOWN: + case KeyEvent.KEYCODE_VOLUME_MUTE: + case KeyEvent.KEYCODE_CAMERA: + case KeyEvent.KEYCODE_FOCUS: + case KeyEvent.KEYCODE_STEM_1: + case KeyEvent.KEYCODE_STEM_2: + case KeyEvent.KEYCODE_STEM_3: return true; } return false; diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index a8d0b90..e0c9d59 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -977,6 +977,22 @@ public final class MotionEvent extends InputEvent implements Parcelable { public static final int AXIS_TILT = 25; /** + * Axis constant: Generic scroll axis of a motion event. + * <p> + * <ul> + * <li>Reports the relative movement of the generic scrolling device. + * </ul> + * </p><p> + * This axis should be used for scroll events that are neither strictly vertical nor horizontal. + * A good example would be the rotation of a rotary encoder input device. + * </p> + * + * @see #getAxisValue(int, int) + * {@hide} + */ + public static final int AXIS_SCROLL = 26; + + /** * Axis constant: Generic 1 axis of a motion event. * The interpretation of a generic axis is device-specific. * @@ -1186,6 +1202,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { names.append(AXIS_BRAKE, "AXIS_BRAKE"); names.append(AXIS_DISTANCE, "AXIS_DISTANCE"); names.append(AXIS_TILT, "AXIS_TILT"); + names.append(AXIS_SCROLL, "AXIS_SCROLL"); names.append(AXIS_GENERIC_1, "AXIS_GENERIC_1"); names.append(AXIS_GENERIC_2, "AXIS_GENERIC_2"); names.append(AXIS_GENERIC_3, "AXIS_GENERIC_3"); diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index bcf9b2c..e839468 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -58,6 +58,11 @@ public class SurfaceControl { private static native void nativeSetWindowCrop(long nativeObject, int l, int t, int r, int b); private static native void nativeSetLayerStack(long nativeObject, int layerStack); + private static native void nativeSetBlur(long nativeObject, float blur); + private static native void nativeSetBlurMaskSurface(long nativeObject, long maskLayerNativeObject); + private static native void nativeSetBlurMaskSampling(long nativeObject, int blurMaskSampling); + private static native void nativeSetBlurMaskAlphaThreshold(long nativeObject, float alpha); + private static native boolean nativeClearContentFrameStats(long nativeObject); private static native boolean nativeGetContentFrameStats(long nativeObject, WindowContentFrameStats outStats); private static native boolean nativeClearAnimationFrameStats(); @@ -170,6 +175,11 @@ public class SurfaceControl { public static final int FX_SURFACE_NORMAL = 0x00000000; /** + * Surface creation flag: Creates a blur surface. + */ + public static final int FX_SURFACE_BLUR = 0x00010000; + + /** * Surface creation flag: Creates a Dim surface. * Everything behind this surface is dimmed by the amount specified * in {@link #setAlpha}. It is an error to lock a Dim surface, since it @@ -215,6 +225,12 @@ public class SurfaceControl { */ public static final int BUILT_IN_DISPLAY_ID_HDMI = 1; + /** + * Built-in physical display id: Attached HDMI display. + * Use only with {@link SurfaceControl#getBuiltInDisplay(int)}. + */ + public static final int BUILT_IN_DISPLAY_ID_TERTIARY = 2; + /* Display power modes * / /** @@ -378,6 +394,29 @@ public class SurfaceControl { nativeSetSize(mNativeObject, w, h); } + public void setBlur(float blur) { + checkNotReleased(); + nativeSetBlur(mNativeObject, blur); + } + + public void setBlurMaskSurface(SurfaceControl maskSurface) { + checkNotReleased(); + if (maskSurface != null) { + maskSurface.checkNotReleased(); + } + nativeSetBlurMaskSurface(mNativeObject, maskSurface == null ? 0:maskSurface.mNativeObject); + } + + public void setBlurMaskSampling(int blurMaskSampling) { + checkNotReleased(); + nativeSetBlurMaskSampling(mNativeObject, blurMaskSampling); + } + + public void setBlurMaskAlphaThreshold(float alpha) { + checkNotReleased(); + nativeSetBlurMaskAlphaThreshold(mNativeObject, alpha); + } + public void hide() { checkNotReleased(); nativeSetFlags(mNativeObject, SURFACE_HIDDEN, SURFACE_HIDDEN); diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index f6119e2..db147ab 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -145,9 +145,10 @@ public class ThreadedRenderer extends HardwareRenderer { @Override boolean initialize(Surface surface) throws OutOfResourcesException { + boolean status = !mInitialized; mInitialized = true; updateEnabledState(surface); - boolean status = nInitialize(mNativeProxy, surface); + nInitialize(mNativeProxy, surface); return status; } @@ -503,7 +504,7 @@ public class ThreadedRenderer extends HardwareRenderer { private static native boolean nLoadSystemProperties(long nativeProxy); private static native void nSetName(long nativeProxy, String name); - private static native boolean nInitialize(long nativeProxy, Surface window); + private static native void nInitialize(long nativeProxy, Surface window); private static native void nUpdateSurface(long nativeProxy, Surface window); private static native boolean nPauseSurface(long nativeProxy, Surface window); private static native void nSetup(long nativeProxy, int width, int height, diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index dea004e..4b56352 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -9284,6 +9284,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final int actionMasked = event.getActionMasked(); if (actionMasked == MotionEvent.ACTION_DOWN) { + android.util.SeempLog.record(3); // Defensive cleanup for new gesture stopNestedScroll(); } @@ -9828,6 +9829,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param event The KeyEvent object that defines the button action. */ public boolean onKeyDown(int keyCode, KeyEvent event) { + android.util.SeempLog.record(4); boolean result = false; if (KeyEvent.isConfirmKey(keyCode)) { @@ -9872,6 +9874,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param event The KeyEvent object that defines the button action. */ public boolean onKeyUp(int keyCode, KeyEvent event) { + android.util.SeempLog.record(5); if (KeyEvent.isConfirmKey(keyCode)) { if ((mViewFlags & ENABLED_MASK) == DISABLED) { return true; @@ -10269,6 +10272,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return True if the event was handled, false otherwise. */ public boolean onTouchEvent(MotionEvent event) { + android.util.SeempLog.record(3); final float x = event.getX(); final float y = event.getY(); final int viewFlags = mViewFlags; @@ -15940,8 +15944,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, RenderNode renderNode = null; Bitmap cache = null; int layerType = getLayerType(); // TODO: signify cache state with just 'cache' local - if (layerType == LAYER_TYPE_SOFTWARE - || (!drawingWithRenderNode && layerType != LAYER_TYPE_NONE)) { + if (!drawingWithRenderNode && layerType != LAYER_TYPE_NONE) { // If not drawing with RenderNode, treat HW layers as SW layerType = LAYER_TYPE_SOFTWARE; buildDrawingCache(true); @@ -16937,8 +16940,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @CallSuper protected boolean verifyDrawable(Drawable who) { - return who == mBackground || (mScrollCache != null && mScrollCache.scrollBar == who) - || (mForegroundInfo != null && mForegroundInfo.mDrawable == who); + // Avoid verifying the scroll bar drawable so that we don't end up in + // an invalidation loop. This effectively prevents the scroll bar + // drawable from triggering invalidations and scheduling runnables. + return who == mBackground || (mForegroundInfo != null && mForegroundInfo.mDrawable == who); } /** @@ -21717,7 +21722,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Last global system UI visibility reported by the window manager. */ - int mGlobalSystemUiVisibility; + int mGlobalSystemUiVisibility = -1; /** * True if a view in this hierarchy has an OnSystemUiVisibilityChangeListener diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index 4d584a3..93f5779 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -317,7 +317,7 @@ public class ViewConfiguration { case HAS_PERMANENT_MENU_KEY_AUTODETECT: { IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); try { - sHasPermanentMenuKey = !wm.hasNavigationBar(); + sHasPermanentMenuKey = wm.hasPermanentMenuKey(); sHasPermanentMenuKeySet = true; } catch (RemoteException ex) { sHasPermanentMenuKey = false; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 42402eb..9569422 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -174,6 +174,10 @@ public final class ViewRootImpl implements ViewParent, // so the window should no longer be active. boolean mStopped = false; + // Set to true if the owner of this window is in ambient mode, + // which means it won't receive input events. + boolean mIsAmbientMode = false; + // Set to true to stop input during an Activity Transition. boolean mPausedForTransition = false; @@ -990,6 +994,10 @@ public final class ViewRootImpl implements ViewParent, } } + public void setIsAmbientMode(boolean ambient) { + mIsAmbientMode = ambient; + } + void setWindowStopped(boolean stopped) { if (mStopped != stopped) { mStopped = stopped; @@ -3704,7 +3712,7 @@ public final class ViewRootImpl implements ViewParent, return true; } else if ((!mAttachInfo.mHasWindowFocus && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) || mStopped - || (mPausedForTransition && !isBack(q.mEvent))) { + || mIsAmbientMode || (mPausedForTransition && !isBack(q.mEvent))) { // This is a focus event and the window doesn't currently have input focus or // has stopped. This could be an event that came back from the previous stage // but the window has lost focus or stopped in the meantime. @@ -5514,6 +5522,8 @@ public final class ViewRootImpl implements ViewParent, writer.println(mProcessInputEventsScheduled); writer.print(innerPrefix); writer.print("mTraversalScheduled="); writer.print(mTraversalScheduled); + writer.print(innerPrefix); writer.print("mIsAmbientMode="); + writer.print(mIsAmbientMode); if (mTraversalScheduled) { writer.print(" (barrier="); writer.print(mTraversalBarrier); writer.println(")"); } else { diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index c222a82..65577f0 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -199,6 +199,7 @@ public abstract class Window { private boolean mHaveWindowFormat = false; private boolean mHaveDimAmount = false; + private boolean mHaveBlurAmount = false; private int mDefaultWindowFormat = PixelFormat.OPAQUE; private boolean mHasSoftInputMode = false; @@ -818,6 +819,13 @@ public abstract class Window { setPrivateFlags(flags, flags); } + /** @hide */ + public void setBlurMaskAlphaThreshold(float alpha) { + final WindowManager.LayoutParams attrs = getAttributes(); + attrs.blurMaskAlphaThreshold = alpha; + dispatchWindowAttributesChanged(attrs); + } + /** * Convenience function to clear the flag bits as specified in flags, as * per {@link #setFlags}. @@ -829,6 +837,11 @@ public abstract class Window { setFlags(0, flags); } + /** @hide */ + public void clearPrivateFlags(int flags) { + setPrivateFlags(0, flags); + } + /** * Set the flags of the window, as per the * {@link WindowManager.LayoutParams WindowManager.LayoutParams} @@ -856,6 +869,13 @@ public abstract class Window { } private void setPrivateFlags(int flags, int mask) { + int preventFlags = WindowManager.LayoutParams.PRIVATE_FLAG_PREVENT_POWER_KEY | + WindowManager.LayoutParams.PRIVATE_FLAG_PREVENT_SYSTEM_KEYS; + + if ((flags & mask & preventFlags) != 0) { + mContext.enforceCallingOrSelfPermission("android.permission.PREVENT_SYSTEM_KEYS", + "No permission to prevent system key"); + } final WindowManager.LayoutParams attrs = getAttributes(); attrs.privateFlags = (attrs.privateFlags & ~mask) | (flags & mask); dispatchWindowAttributesChanged(attrs); @@ -895,6 +915,19 @@ public abstract class Window { } /** + * Set the amount of blur behind the window when using + * {@link WindowManager.LayoutParams#FLAG_BLUR_BEHIND}. + * This feature may not be supported by all devices. + * {@hide} + */ + public void setBlurAmount(float amount) { + final WindowManager.LayoutParams attrs = getAttributes(); + attrs.blurAmount = amount; + mHaveBlurAmount = true; + dispatchWindowAttributesChanged(attrs); + } + + /** * Specify custom window attributes. <strong>PLEASE NOTE:</strong> the * layout params you give here should generally be from values previously * retrieved with {@link #getAttributes()}; you probably do not want to @@ -1404,6 +1437,11 @@ public abstract class Window { return mHaveDimAmount; } + /** @hide */ + protected boolean haveBlurAmount() { + return mHaveBlurAmount; + } + public abstract void setChildDrawable(int featureId, Drawable drawable); public abstract void setChildInt(int featureId, int value); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 45bc1df..36f593e 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -186,6 +186,7 @@ public interface WindowManager extends ViewManager { * @see #TYPE_SYSTEM_ERROR * @see #TYPE_INPUT_METHOD * @see #TYPE_INPUT_METHOD_DIALOG + * @see #TYPE_KEYGUARD_PANEL */ @ViewDebug.ExportedProperty(mapping = { @ViewDebug.IntToString(from = TYPE_BASE_APPLICATION, to = "TYPE_BASE_APPLICATION"), @@ -226,6 +227,7 @@ public interface WindowManager extends ViewManager { @ViewDebug.IntToString(from = TYPE_PRIVATE_PRESENTATION, to = "TYPE_PRIVATE_PRESENTATION"), @ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION, to = "TYPE_VOICE_INTERACTION"), @ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION_STARTING, to = "TYPE_VOICE_INTERACTION_STARTING"), + @ViewDebug.IntToString(from = TYPE_KEYGUARD_PANEL, to = "TYPE_KEYGUARD_PANEL"), }) public int type; @@ -565,6 +567,13 @@ public interface WindowManager extends ViewManager { public static final int TYPE_VOICE_INTERACTION_STARTING = FIRST_SYSTEM_WINDOW+33; /** + * Window type: Windows that are layered within the keyguard + * This type is LAST_SYSTEM_WINDOW-1 to avoid future conflicts with AOSP + * @hide + */ + public static final int TYPE_KEYGUARD_PANEL = FIRST_SYSTEM_WINDOW+998; + + /** * End of types of system windows. */ public static final int LAST_SYSTEM_WINDOW = 2999; @@ -1128,6 +1137,48 @@ public interface WindowManager extends ViewManager { public static final int PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT = 0x00001000; /** + * {@hide} + */ + public static final int PRIVATE_FLAG_STATUS_HIDE_FORCED = 0x00800000; + + /** + * {@hide} + */ + public static final int PRIVATE_FLAG_NAV_HIDE_FORCED = 0x01000000; + + /** + * The window had not set FULLSCREEN flag so don't handle it as fullscreen in layoutWindowLw + * + * {@hide} + */ + public static final int PRIVATE_FLAG_WAS_NOT_FULLSCREEN = 0x02000000; + + /** + * Window flag: Overrides default system key behavior. + * {@hide} + */ + public static final int PRIVATE_FLAG_PREVENT_SYSTEM_KEYS = 0x10000000; + + /** + * Window flag: Overrides default system key behavior. + * {@hide} + */ + public static final int PRIVATE_FLAG_PREVENT_POWER_KEY = 0x20000000; + + /** + * Window flag: adding additional blur layer and set this as masking layer + * {@hide} + */ + public static final int PRIVATE_FLAG_BLUR_WITH_MASKING = 0x40000000; + + /** + * Window flag: adding additional blur layer and set this as masking layer. + * This is faster and ugglier than non-scaled version. + * {@hide} + */ + public static final int PRIVATE_FLAG_BLUR_WITH_MASKING_SCALED = 0x80000000; + + /** * Control flags that are private to the platform. * @hide */ @@ -1389,6 +1440,15 @@ public interface WindowManager extends ViewManager { public float dimAmount = 1.0f; /** + * When {@link #FLAG_BLUR_BEHIND} is set, this is the amount of blur + * to apply. Range is from 1.0 for maximum to 0.0 for no + * blur. + * @hide + */ + public float blurAmount = 1.0f; + + + /** * Default value for {@link #screenBrightness} and {@link #buttonBrightness} * indicating that the brightness value is not overridden for this window * and normal brightness policy should be used. @@ -1581,6 +1641,14 @@ public interface WindowManager extends ViewManager { */ public long userActivityTimeout = -1; + /** + * Threshold value that blur masking layer uses to determine whether + * to use or discard the blurred color. + * Value should be between 0.0 and 1.0 + * @hide + */ + public float blurMaskAlphaThreshold = 0.0f; + public LayoutParams() { super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); type = TYPE_APPLICATION; @@ -1667,6 +1735,7 @@ public interface WindowManager extends ViewManager { out.writeInt(windowAnimations); out.writeFloat(alpha); out.writeFloat(dimAmount); + out.writeFloat(blurAmount); out.writeFloat(screenBrightness); out.writeFloat(buttonBrightness); out.writeInt(rotationAnimation); @@ -1687,6 +1756,7 @@ public interface WindowManager extends ViewManager { out.writeInt(surfaceInsets.bottom); out.writeInt(hasManualSurfaceInsets ? 1 : 0); out.writeInt(needsMenuKey); + out.writeFloat(blurMaskAlphaThreshold); } public static final Parcelable.Creator<LayoutParams> CREATOR @@ -1717,6 +1787,7 @@ public interface WindowManager extends ViewManager { windowAnimations = in.readInt(); alpha = in.readFloat(); dimAmount = in.readFloat(); + blurAmount = in.readFloat(); screenBrightness = in.readFloat(); buttonBrightness = in.readFloat(); rotationAnimation = in.readInt(); @@ -1737,6 +1808,7 @@ public interface WindowManager extends ViewManager { surfaceInsets.bottom = in.readInt(); hasManualSurfaceInsets = in.readInt() != 0; needsMenuKey = in.readInt(); + blurMaskAlphaThreshold = in.readFloat(); } @SuppressWarnings({"PointlessBitwiseExpression"}) @@ -1776,6 +1848,10 @@ public interface WindowManager extends ViewManager { /** {@hide} */ public static final int PREFERRED_DISPLAY_MODE_ID = 1 << 23; /** {@hide} */ + public static final int BLUR_AMOUNT_CHANGED = 1 << 29; + /** {@hide} */ + public static final int BLUR_MASK_ALPHA_THRESHOLD_CHANGED = 1 << 30; + /** {@hide} */ public static final int EVERYTHING_CHANGED = 0xffffffff; // internal buffer to backup/restore parameters under compatibility mode. @@ -1870,6 +1946,10 @@ public interface WindowManager extends ViewManager { dimAmount = o.dimAmount; changes |= DIM_AMOUNT_CHANGED; } + if (blurAmount != o.blurAmount) { + blurAmount = o.blurAmount; + changes |= BLUR_AMOUNT_CHANGED; + } if (screenBrightness != o.screenBrightness) { screenBrightness = o.screenBrightness; changes |= SCREEN_BRIGHTNESS_CHANGED; @@ -1935,6 +2015,11 @@ public interface WindowManager extends ViewManager { changes |= NEEDS_MENU_KEY_CHANGED; } + if (blurMaskAlphaThreshold != o.blurMaskAlphaThreshold) { + blurMaskAlphaThreshold = o.blurMaskAlphaThreshold; + changes |= BLUR_MASK_ALPHA_THRESHOLD_CHANGED; + } + return changes; } diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index 98e9f54..95e291c 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -81,12 +81,14 @@ public final class WindowManagerImpl implements WindowManager { @Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { + android.util.SeempLog.record_vg_layout(383,params); applyDefaultToken(params); mGlobal.addView(view, params, mDisplay, mParentWindow); } @Override public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { + android.util.SeempLog.record_vg_layout(384,params); applyDefaultToken(params); mGlobal.updateViewLayout(view, params); } diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index aaf6052..d19096b 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -453,6 +453,8 @@ public interface WindowManagerPolicy { /** Unregister a system listener for touch events */ void unregisterPointerEventListener(PointerEventListener listener); + + void addSystemUIVisibilityFlag(int flags); } public interface PointerEventListener { @@ -663,6 +665,11 @@ public interface WindowManagerPolicy { public WindowState getWinShowWhenLockedLw(); /** + * Returns the current keyguard panel, if such a thing exists. + */ + public WindowState getWinKeyguardPanelLw(); + + /** * Called when the system would like to show a UI to indicate that an * application is starting. You can use this to add a * APPLICATION_STARTING_TYPE window with the given appToken to the window @@ -778,7 +785,8 @@ public interface WindowManagerPolicy { * Create and return an animation to let the wallpaper disappear after being shown on a force * hiding window. */ - public Animation createForceHideWallpaperExitAnimation(boolean goingToNotificationShade); + public Animation createForceHideWallpaperExitAnimation(boolean goingToNotificationShade, + boolean keyguardShowingMedia); /** * Called from the input reader thread before a key is enqueued. @@ -1237,6 +1245,12 @@ public interface WindowManagerPolicy { * Specifies whether there is an on-screen navigation bar separate from the status bar. */ public boolean hasNavigationBar(); + public boolean hasPermanentMenuKey(); + + /** + * Specifies whether the device needs a navigation bar (because it has no hardware buttons) + */ + public boolean needsNavigationBar(); /** * Lock the device now. @@ -1311,4 +1325,6 @@ public interface WindowManagerPolicy { * @param fadeoutDuration the duration of the exit animation, in milliseconds */ public void startKeyguardExitAnimation(long startTime, long fadeoutDuration); + + public void setLiveLockscreenEdgeDetector(boolean enable); } diff --git a/core/java/android/view/WindowManagerPolicyControl.java b/core/java/android/view/WindowManagerPolicyControl.java new file mode 100644 index 0000000..cdba239 --- /dev/null +++ b/core/java/android/view/WindowManagerPolicyControl.java @@ -0,0 +1,451 @@ +/* + * 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.view; + +import android.app.ActivityManager; +import android.content.Context; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.ArraySet; +import android.util.Slog; +import android.view.WindowManager.LayoutParams; +import android.view.WindowManagerPolicy.WindowState; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Iterator; + +/** + * Runtime adjustments applied to the global window policy. + * + * This includes forcing immersive mode behavior for one or both system bars (based on a package + * list) and permanently disabling immersive mode confirmations for specific packages. + * + * Control by setting {@link Settings.Global.POLICY_CONTROL} to one or more name-value pairs. + * e.g. + * to force immersive mode everywhere: + * "immersive.full=*" + * to force transient status for all apps except a specific package: + * "immersive.status=apps,-com.package" + * to disable the immersive mode confirmations for specific packages: + * "immersive.preconfirms=com.package.one,com.package.two" + * + * Separate multiple name-value pairs with ':' + * e.g. "immersive.status=apps:immersive.preconfirms=*" + * + * @hide + */ +public class WindowManagerPolicyControl { + private static String TAG = "PolicyControl"; + private static boolean DEBUG = false; + + private static final String NAME_IMMERSIVE_FULL = "immersive.full"; + private static final String NAME_IMMERSIVE_STATUS = "immersive.status"; + private static final String NAME_IMMERSIVE_NAVIGATION = "immersive.navigation"; + private static final String NAME_IMMERSIVE_PRECONFIRMATIONS = "immersive.preconfirms"; + + private static int sDefaultImmersiveStyle; + private static String sSettingValue; + private static Filter sImmersivePreconfirmationsFilter; + private static Filter sImmersiveStatusFilter; + private static Filter sImmersiveNavigationFilter; + + /** + * Accessible constants for Settings + */ + public final static class ImmersiveDefaultStyles { + public final static int IMMERSIVE_FULL = 0; + public final static int IMMERSIVE_STATUS = 1; + public final static int IMMERSIVE_NAVIGATION = 2; + } + + public static int getSystemUiVisibility(WindowState win, LayoutParams attrs) { + attrs = attrs != null ? attrs : win.getAttrs(); + int vis = win != null ? win.getSystemUiVisibility() : attrs.systemUiVisibility; + if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs) + && (sDefaultImmersiveStyle == ImmersiveDefaultStyles.IMMERSIVE_FULL || + sDefaultImmersiveStyle == ImmersiveDefaultStyles.IMMERSIVE_STATUS)) { + vis |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + vis &= ~(View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.STATUS_BAR_TRANSLUCENT); + } + if (sImmersiveNavigationFilter != null && sImmersiveNavigationFilter.matches(attrs) + && (sDefaultImmersiveStyle == ImmersiveDefaultStyles.IMMERSIVE_FULL || + sDefaultImmersiveStyle == ImmersiveDefaultStyles.IMMERSIVE_NAVIGATION)) { + vis |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + vis &= ~(View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.NAVIGATION_BAR_TRANSLUCENT); + } + return vis; + } + + public static int getWindowFlags(WindowState win, LayoutParams attrs) { + attrs = attrs != null ? attrs : win.getAttrs(); + int flags = attrs.flags; + + if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs) + && (sDefaultImmersiveStyle == ImmersiveDefaultStyles.IMMERSIVE_FULL || + sDefaultImmersiveStyle == ImmersiveDefaultStyles.IMMERSIVE_STATUS)) { + flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; + flags &= ~(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS + | WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); + } + if (sImmersiveNavigationFilter != null && sImmersiveNavigationFilter.matches(attrs) + && (sDefaultImmersiveStyle == ImmersiveDefaultStyles.IMMERSIVE_FULL || + sDefaultImmersiveStyle == ImmersiveDefaultStyles.IMMERSIVE_NAVIGATION)) { + flags &= ~WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; + } + return flags; + } + + public static int getPrivateWindowFlags(WindowState win, LayoutParams attrs) { + attrs = attrs != null ? attrs : win.getAttrs(); + int privateFlags = attrs.privateFlags; + + if (sImmersiveStatusFilter != null && sImmersiveNavigationFilter != null && + sImmersiveStatusFilter.isEnabledForAll() + && sImmersiveNavigationFilter.isEnabledForAll()) { + + if ((attrs.flags & LayoutParams.FLAG_FULLSCREEN) == 0) { + privateFlags |= LayoutParams.PRIVATE_FLAG_WAS_NOT_FULLSCREEN; + } + + switch (sDefaultImmersiveStyle) { + case ImmersiveDefaultStyles.IMMERSIVE_FULL: + privateFlags |= LayoutParams.PRIVATE_FLAG_NAV_HIDE_FORCED; + privateFlags |= LayoutParams.PRIVATE_FLAG_STATUS_HIDE_FORCED; + return privateFlags; + case ImmersiveDefaultStyles.IMMERSIVE_STATUS: + privateFlags |= LayoutParams.PRIVATE_FLAG_STATUS_HIDE_FORCED; + return privateFlags; + case ImmersiveDefaultStyles.IMMERSIVE_NAVIGATION: + privateFlags |= LayoutParams.PRIVATE_FLAG_NAV_HIDE_FORCED; + return privateFlags; + } + } + + if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs)) { + if ((attrs.flags & LayoutParams.FLAG_FULLSCREEN) == 0) { + privateFlags |= LayoutParams.PRIVATE_FLAG_WAS_NOT_FULLSCREEN; + } + privateFlags |= LayoutParams.PRIVATE_FLAG_STATUS_HIDE_FORCED; + } + + if (sImmersiveNavigationFilter != null && sImmersiveNavigationFilter.matches(attrs)) { + privateFlags |= LayoutParams.PRIVATE_FLAG_NAV_HIDE_FORCED; + } + + return privateFlags; + } + + public static boolean immersiveStatusFilterMatches(String packageName) { + return sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(packageName); + } + + public static boolean immersiveNavigationFilterMatches(String packageName) { + return sImmersiveNavigationFilter != null + && sImmersiveNavigationFilter.matches(packageName); + } + + public static int adjustClearableFlags(WindowState win, int clearableFlags) { + final LayoutParams attrs = win != null ? win.getAttrs() : null; + if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs)) { + clearableFlags &= ~View.SYSTEM_UI_FLAG_FULLSCREEN; + } + return clearableFlags; + } + + public static boolean disableImmersiveConfirmation(String pkg) { + return (sImmersivePreconfirmationsFilter != null + && sImmersivePreconfirmationsFilter.matches(pkg)) + || ActivityManager.isRunningInTestHarness(); + } + + public static void reloadFromSetting(Context context) { + reloadStyleFromSetting(context, Settings.Global.POLICY_CONTROL_STYLE); + reloadFromSetting(context, Settings.Global.POLICY_CONTROL); + } + + public static void reloadFromSetting(Context context, String key) { + if (DEBUG) Slog.d(TAG, "reloadFromSetting()"); + String value = null; + try { + value = Settings.Global.getStringForUser(context.getContentResolver(), + key, + UserHandle.USER_CURRENT); + if (sSettingValue != null && sSettingValue.equals(value)) return; + setFilters(value); + sSettingValue = value; + } catch (Throwable t) { + Slog.w(TAG, "Error loading policy control, value=" + value, t); + } + } + + public static void reloadStyleFromSetting(Context context, String key) { + sDefaultImmersiveStyle = Settings.Global.getInt(context.getContentResolver(), + key, WindowManagerPolicyControl.ImmersiveDefaultStyles.IMMERSIVE_FULL); + if (DEBUG) Slog.d(TAG, "reloadStyleFromSetting " + sDefaultImmersiveStyle); + } + + public static void saveToSettings(Context context) { + saveToSettings(context, Settings.Global.POLICY_CONTROL); + } + + public static void saveToSettings(Context context, String key) { + StringBuilder value = new StringBuilder(); + boolean needSemicolon = false; + if (sImmersiveStatusFilter != null) { + writeFilter(NAME_IMMERSIVE_STATUS, sImmersiveStatusFilter, value); + needSemicolon = true; + } + if (sImmersiveNavigationFilter != null) { + if (needSemicolon) { + value.append(":"); + } + writeFilter(NAME_IMMERSIVE_NAVIGATION, sImmersiveNavigationFilter, value); + } + + Settings.Global.putString(context.getContentResolver(), key, value.toString()); + } + + public static void saveStyleToSettings(Context context, int value) { + Settings.Global.putInt(context.getContentResolver(), + Settings.Global.POLICY_CONTROL_STYLE, value); + sDefaultImmersiveStyle = value; + } + + public static void addToStatusWhiteList(String packageName) { + if (sImmersiveStatusFilter == null) { + sImmersiveStatusFilter = new Filter(new ArraySet<String>(), new ArraySet<String>()); + } + + if (!sImmersiveStatusFilter.mWhitelist.contains(packageName)) { + sImmersiveStatusFilter.mWhitelist.add(packageName); + } + } + + public static void addToNavigationWhiteList(String packageName) { + if (sImmersiveNavigationFilter == null) { + sImmersiveNavigationFilter = new Filter(new ArraySet<String>(), new ArraySet<String>()); + } + + if (!sImmersiveNavigationFilter.mWhitelist.contains(packageName)) { + sImmersiveNavigationFilter.mWhitelist.add(packageName); + } + } + + public static void removeFromWhiteLists(String packageName) { + if (sImmersiveStatusFilter != null) { + sImmersiveStatusFilter.mWhitelist.remove(packageName); + } + if (sImmersiveNavigationFilter != null) { + sImmersiveNavigationFilter.mWhitelist.remove(packageName); + } + } + + public static ArraySet<String> getWhiteLists() { + ArraySet<String> result = new ArraySet<>(); + + if (sImmersiveStatusFilter != null) { + result.addAll(sImmersiveStatusFilter.mWhitelist); + } + if (sImmersiveNavigationFilter != null + && sImmersiveNavigationFilter != sImmersiveStatusFilter) { + result.addAll(sImmersiveNavigationFilter.mWhitelist); + } + + return result; + } + + private static void writeFilter(String name, Filter filter, StringBuilder stringBuilder) { + if (filter.mWhitelist.isEmpty() && filter.mBlacklist.isEmpty()) { + return; + } + stringBuilder.append(name); + stringBuilder.append("="); + + boolean needComma = false; + if (!filter.mWhitelist.isEmpty()) { + writePackages(filter.mWhitelist, stringBuilder, false); + needComma = true; + } + if (!filter.mBlacklist.isEmpty()) { + if (needComma) { + stringBuilder.append(","); + } + writePackages(filter.mBlacklist, stringBuilder, true); + } + } + + private static void writePackages(ArraySet<String> set, StringBuilder stringBuilder, + boolean isBlackList) { + Iterator<String> iterator = set.iterator(); + while (iterator.hasNext()) { + if (isBlackList) { + stringBuilder.append("-"); + } + String name = iterator.next(); + stringBuilder.append(name); + if (iterator.hasNext()) { + stringBuilder.append(","); + } + } + } + + public static boolean isImmersiveFiltersActive() { + return sImmersiveStatusFilter != null || sImmersiveNavigationFilter != null; + } + + public static void dump(String prefix, PrintWriter pw) { + dump("sImmersiveStatusFilter", sImmersiveStatusFilter, prefix, pw); + dump("sImmersiveNavigationFilter", sImmersiveNavigationFilter, prefix, pw); + dump("sImmersivePreconfirmationsFilter", sImmersivePreconfirmationsFilter, prefix, pw); + } + + private static void dump(String name, Filter filter, String prefix, PrintWriter pw) { + pw.print(prefix); pw.print("PolicyControl."); pw.print(name); pw.print('='); + if (filter == null) { + pw.println("null"); + } else { + filter.dump(pw); pw.println(); + } + } + + private static void setFilters(String value) { + if (DEBUG) Slog.d(TAG, "setFilters: " + value); + sImmersiveStatusFilter = null; + sImmersiveNavigationFilter = null; + sImmersivePreconfirmationsFilter = null; + if (value != null) { + String[] nvps = value.split(":"); + for (String nvp : nvps) { + int i = nvp.indexOf('='); + if (i == -1) continue; + String n = nvp.substring(0, i); + String v = nvp.substring(i + 1); + if (n.equals(NAME_IMMERSIVE_FULL)) { + Filter f = Filter.parse(v); + sImmersiveStatusFilter = sImmersiveNavigationFilter = f; + if (sImmersivePreconfirmationsFilter == null) { + sImmersivePreconfirmationsFilter = f; + } + } else if (n.equals(NAME_IMMERSIVE_STATUS)) { + Filter f = Filter.parse(v); + sImmersiveStatusFilter = f; + } else if (n.equals(NAME_IMMERSIVE_NAVIGATION)) { + Filter f = Filter.parse(v); + sImmersiveNavigationFilter = f; + if (sImmersivePreconfirmationsFilter == null) { + sImmersivePreconfirmationsFilter = f; + } + } else if (n.equals(NAME_IMMERSIVE_PRECONFIRMATIONS)) { + Filter f = Filter.parse(v); + sImmersivePreconfirmationsFilter = f; + } + } + } + if (DEBUG) { + Slog.d(TAG, "immersiveStatusFilter: " + sImmersiveStatusFilter); + Slog.d(TAG, "immersiveNavigationFilter: " + sImmersiveNavigationFilter); + Slog.d(TAG, "immersivePreconfirmationsFilter: " + sImmersivePreconfirmationsFilter); + } + } + + private static class Filter { + private static final String ALL = "*"; + private static final String APPS = "apps"; + + private final ArraySet<String> mWhitelist; + private final ArraySet<String> mBlacklist; + + private Filter(ArraySet<String> whitelist, ArraySet<String> blacklist) { + mWhitelist = whitelist; + mBlacklist = blacklist; + } + + boolean matches(LayoutParams attrs) { + if (attrs == null) return false; + boolean isApp = attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW + && attrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; + if (isApp && mBlacklist.contains(APPS)) return false; + if (onBlacklist(attrs.packageName)) return false; + if (isApp && mWhitelist.contains(APPS)) return true; + return onWhitelist(attrs.packageName); + } + + boolean matches(String packageName) { + return !onBlacklist(packageName) && onWhitelist(packageName); + } + + public boolean isEnabledForAll() { + return mWhitelist.contains(ALL); + } + + private boolean onBlacklist(String packageName) { + return mBlacklist.contains(packageName) || mBlacklist.contains(ALL); + } + + private boolean onWhitelist(String packageName) { + return mWhitelist.contains(ALL) || mWhitelist.contains(packageName); + } + + void dump(PrintWriter pw) { + pw.print("Filter["); + dump("whitelist", mWhitelist, pw); pw.print(','); + dump("blacklist", mBlacklist, pw); pw.print(']'); + } + + private void dump(String name, ArraySet<String> set, PrintWriter pw) { + pw.print(name); pw.print("=("); + final int n = set.size(); + for (int i = 0; i < n; i++) { + if (i > 0) pw.print(','); + pw.print(set.valueAt(i)); + } + pw.print(')'); + } + + @Override + public String toString() { + StringWriter sw = new StringWriter(); + dump(new PrintWriter(sw, true)); + return sw.toString(); + } + + // value = comma-delimited list of tokens, where token = (package name|apps|*) + // e.g. "com.package1", or "apps, com.android.keyguard" or "*" + static Filter parse(String value) { + if (value == null) return null; + ArraySet<String> whitelist = new ArraySet<String>(); + ArraySet<String> blacklist = new ArraySet<String>(); + for (String token : value.split(",")) { + token = token.trim(); + if (token.startsWith("-") && token.length() > 1) { + token = token.substring(1); + blacklist.add(token); + } else { + whitelist.add(token); + } + } + return new Filter(whitelist, blacklist); + } + } +} diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java index 4737e9b..01fcc1a 100644 --- a/core/java/android/webkit/WebChromeClient.java +++ b/core/java/android/webkit/WebChromeClient.java @@ -290,7 +290,9 @@ public class WebChromeClient { * origin. */ public void onGeolocationPermissionsShowPrompt(String origin, - GeolocationPermissions.Callback callback) {} + GeolocationPermissions.Callback callback) { + android.util.SeempLog.record(54); + } /** * Notify the host application that a request for Geolocation permissions, diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index 584deff..93b1e56 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -97,7 +97,14 @@ public final class WebViewFactory { } public static String getWebViewPackageName() { - return AppGlobals.getInitialApplication().getString( + Application initialApp = AppGlobals.getInitialApplication(); + String pkg = initialApp.getString( + com.android.internal.R.string.config_alternateWebViewPackageName); + /* Attempt to use alternate WebView package first */ + if (isPackageInstalled(initialApp, pkg)) { + return pkg; + } + return initialApp.getString( com.android.internal.R.string.config_webViewPackageName); } @@ -529,6 +536,14 @@ public final class WebViewFactory { return IWebViewUpdateService.Stub.asInterface(ServiceManager.getService("webviewupdate")); } + private static boolean isPackageInstalled(Context context, String packageName) { + try { + return context.getPackageManager().getPackageInfo(packageName, 0) != null; + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + private static native boolean nativeReserveAddressSpace(long addressSpaceToReserve); private static native boolean nativeCreateRelroFile(String lib32, String lib64, String relro32, String relro64); diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index a4c8d1c..31bc536 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -663,7 +663,14 @@ public abstract class AbsSeekBar extends ProgressBar { progress += scale * max; setHotspot(x, (int) event.getY()); - setProgress((int) progress, true); + setProgress(updateTouchProgress(getProgress(), (int) progress), true); + } + + /** + * @hide + */ + protected int updateTouchProgress(int lastProgress, int newProgress) { + return newProgress; } /** diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java index c40289e..559181b 100644 --- a/core/java/android/widget/FastScroller.java +++ b/core/java/android/widget/FastScroller.java @@ -383,6 +383,7 @@ class FastScroller { break; } } + ta.recycle(); updateAppearance(); } diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index b5e08ca..df3d850 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -348,7 +348,6 @@ public class LinearLayout extends ViewGroup { final int count = getVirtualChildCount(); for (int i = 0; i < count; i++) { final View child = getVirtualChildAt(i); - if (child != null && child.getVisibility() != GONE) { if (hasDividerBeforeChildAt(i)) { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); @@ -377,7 +376,7 @@ public class LinearLayout extends ViewGroup { */ private View getLastNonGoneChild() { for (int i = getVirtualChildCount() - 1; i >= 0; i--) { - View child = getVirtualChildAt(i); + final View child = getVirtualChildAt(i); if (child != null && child.getVisibility() != GONE) { return child; } @@ -390,7 +389,6 @@ public class LinearLayout extends ViewGroup { final boolean isLayoutRtl = isLayoutRtl(); for (int i = 0; i < count; i++) { final View child = getVirtualChildAt(i); - if (child != null && child.getVisibility() != GONE) { if (hasDividerBeforeChildAt(i)) { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); @@ -577,8 +575,9 @@ public class LinearLayout extends ViewGroup { * for an example.</p> * * @param index the child's index - * @return the child at the specified index + * @return the child at the specified index, may be {@code null} */ + @Nullable View getVirtualChildAt(int index) { return getChildAt(index); } @@ -659,7 +658,7 @@ public class LinearLayout extends ViewGroup { */ private boolean allViewsAreGoneBefore(int childIndex) { for (int i = childIndex - 1; i >= 0; i--) { - View child = getVirtualChildAt(i); + final View child = getVirtualChildAt(i); if (child != null && child.getVisibility() != GONE) { return false; } @@ -703,7 +702,6 @@ public class LinearLayout extends ViewGroup { // See how tall everyone is. Also remember max width. for (int i = 0; i < count; ++i) { final View child = getVirtualChildAt(i); - if (child == null) { mTotalLength += measureNullChild(i); continue; @@ -822,7 +820,6 @@ public class LinearLayout extends ViewGroup { for (int i = 0; i < count; ++i) { final View child = getVirtualChildAt(i); - if (child == null) { mTotalLength += measureNullChild(i); continue; @@ -938,7 +935,6 @@ public class LinearLayout extends ViewGroup { if (useLargestChild && heightMode != MeasureSpec.EXACTLY) { for (int i = 0; i < count; i++) { final View child = getVirtualChildAt(i); - if (child == null || child.getVisibility() == View.GONE) { continue; } @@ -981,7 +977,7 @@ public class LinearLayout extends ViewGroup { MeasureSpec.EXACTLY); for (int i = 0; i< count; ++i) { final View child = getVirtualChildAt(i); - if (child.getVisibility() != GONE) { + if (child != null && child.getVisibility() != GONE) { LinearLayout.LayoutParams lp = ((LinearLayout.LayoutParams)child.getLayoutParams()); if (lp.width == LayoutParams.MATCH_PARENT) { @@ -1047,7 +1043,6 @@ public class LinearLayout extends ViewGroup { // See how wide everyone is. Also remember max height. for (int i = 0; i < count; ++i) { final View child = getVirtualChildAt(i); - if (child == null) { mTotalLength += measureNullChild(i); continue; @@ -1203,7 +1198,6 @@ public class LinearLayout extends ViewGroup { for (int i = 0; i < count; ++i) { final View child = getVirtualChildAt(i); - if (child == null) { mTotalLength += measureNullChild(i); continue; @@ -1361,7 +1355,6 @@ public class LinearLayout extends ViewGroup { if (useLargestChild && widthMode != MeasureSpec.EXACTLY) { for (int i = 0; i < count; i++) { final View child = getVirtualChildAt(i); - if (child == null || child.getVisibility() == View.GONE) { continue; } @@ -1406,7 +1399,7 @@ public class LinearLayout extends ViewGroup { MeasureSpec.EXACTLY); for (int i = 0; i < count; ++i) { final View child = getVirtualChildAt(i); - if (child.getVisibility() != GONE) { + if (child != null && child.getVisibility() != GONE) { LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams(); if (lp.height == LayoutParams.MATCH_PARENT) { @@ -1666,9 +1659,8 @@ public class LinearLayout extends ViewGroup { } for (int i = 0; i < count; i++) { - int childIndex = start + dir * i; + final int childIndex = start + dir * i; final View child = getVirtualChildAt(childIndex); - if (child == null) { childLeft += measureNullChild(childIndex); } else if (child.getVisibility() != GONE) { diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 7ca3339..6a272e5 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -31,6 +31,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; +import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.PorterDuff; import android.graphics.Rect; @@ -55,6 +56,8 @@ import android.view.ViewGroup; import android.widget.AdapterView.OnItemClickListener; import libcore.util.Objects; +import com.android.internal.R; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -206,14 +209,22 @@ public class RemoteViews implements Parcelable, Filter { /** @hide */ public static class OnClickHandler { + + private int mEnterAnimationId; + public boolean onClickHandler(View view, PendingIntent pendingIntent, Intent fillInIntent) { try { // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT? Context context = view.getContext(); - ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view, - 0, 0, - view.getMeasuredWidth(), view.getMeasuredHeight()); + ActivityOptions opts; + if (mEnterAnimationId != 0) { + opts = ActivityOptions.makeCustomAnimation(context, mEnterAnimationId, 0); + } else { + opts = ActivityOptions.makeScaleUpAnimation(view, + 0, 0, + view.getMeasuredWidth(), view.getMeasuredHeight()); + } context.startIntentSender( pendingIntent.getIntentSender(), fillInIntent, Intent.FLAG_ACTIVITY_NEW_TASK, @@ -228,6 +239,10 @@ public class RemoteViews implements Parcelable, Filter { } return true; } + + public void setEnterAnimationId(int enterAnimationId) { + mEnterAnimationId = enterAnimationId; + } } /** @@ -2729,6 +2744,12 @@ public class RemoteViews implements Parcelable, Filter { /** @hide */ public View apply(Context context, ViewGroup parent, OnClickHandler handler) { + return apply(context, parent, handler, null); + } + + /** @hide */ + public View apply(Context context, ViewGroup parent, OnClickHandler handler, + String themePackageName) { RemoteViews rvToApply = getRemoteViewsToApply(context); View result; @@ -2736,7 +2757,7 @@ public class RemoteViews implements Parcelable, Filter { // user. So build a context that loads resources from that user but // still returns the current users userId so settings like data / time formats // are loaded without requiring cross user persmissions. - final Context contextForResources = getContextForResources(context); + final Context contextForResources = getContextForResources(context, themePackageName); Context inflationContext = new ContextWrapper(context) { @Override public Resources getResources() { @@ -2761,11 +2782,31 @@ public class RemoteViews implements Parcelable, Filter { inflater.setFilter(this); result = inflater.inflate(rvToApply.getLayoutId(), parent, false); + loadTransitionOverride(context, handler); + rvToApply.performApply(result, parent, handler); return result; } + private static void loadTransitionOverride(Context context, + RemoteViews.OnClickHandler handler) { + if (handler != null && context.getResources().getBoolean( + com.android.internal.R.bool.config_overrideRemoteViewsActivityTransition)) { + TypedArray windowStyle = context.getTheme().obtainStyledAttributes( + com.android.internal.R.styleable.Window); + int windowAnimations = windowStyle.getResourceId( + com.android.internal.R.styleable.Window_windowAnimationStyle, 0); + TypedArray windowAnimationStyle = context.obtainStyledAttributes( + windowAnimations, com.android.internal.R.styleable.WindowAnimation); + handler.setEnterAnimationId(windowAnimationStyle.getResourceId( + com.android.internal.R.styleable. + WindowAnimation_activityOpenRemoteViewsEnterAnimation, 0)); + windowStyle.recycle(); + windowAnimationStyle.recycle(); + } + } + /** * Applies all of the actions to the provided view. * @@ -2806,14 +2847,15 @@ public class RemoteViews implements Parcelable, Filter { } } - private Context getContextForResources(Context context) { + private Context getContextForResources(Context context, String themePackageName) { if (mApplication != null) { if (context.getUserId() == UserHandle.getUserId(mApplication.uid) - && context.getPackageName().equals(mApplication.packageName)) { + && context.getPackageName().equals(mApplication.packageName) + && themePackageName == null) { return context; } try { - return context.createApplicationContext(mApplication, + return context.createApplicationContext(mApplication, themePackageName, Context.CONTEXT_RESTRICTED); } catch (NameNotFoundException e) { Log.e(LOG_TAG, "Package name " + mApplication.packageName + " not found"); diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java index f7f9c91..22931fc 100644 --- a/core/java/android/widget/TableRow.java +++ b/core/java/android/widget/TableRow.java @@ -98,7 +98,7 @@ public class TableRow extends LinearLayout { * {@hide} */ void setColumnCollapsed(int columnIndex, boolean collapsed) { - View child = getVirtualChildAt(columnIndex); + final View child = getVirtualChildAt(columnIndex); if (child != null) { child.setVisibility(collapsed ? GONE : VISIBLE); } diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java index bcde315..d5a3f16 100644 --- a/core/java/android/widget/TextClock.java +++ b/core/java/android/widget/TextClock.java @@ -548,6 +548,7 @@ public class TextClock extends TextView { filter.addAction(Intent.ACTION_TIME_TICK); filter.addAction(Intent.ACTION_TIME_CHANGED); filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); + filter.addAction(Intent.ACTION_DOZE_PULSE_STARTING); getContext().registerReceiver(mIntentReceiver, filter, null, getHandler()); } diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java index 207f675..e8dccab 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -18,12 +18,15 @@ package android.widget; import android.annotation.IntDef; import android.annotation.StringRes; +import android.app.ActivityManager; import android.app.INotificationManager; import android.app.ITransientNotification; import android.content.Context; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.PixelFormat; +import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; @@ -400,6 +403,22 @@ public class Toast { if (context == null) { context = mView.getContext(); } + + ImageView appIcon = (ImageView) mView.findViewById(android.R.id.icon); + if (appIcon != null) { + ActivityManager am = + (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + if (!am.isPackageInForeground(packageName)) { + PackageManager pm = context.getPackageManager(); + Drawable icon = null; + try { + icon = pm.getApplicationIcon(packageName); + } catch (PackageManager.NameNotFoundException e) { + // nothing to do + } + appIcon.setImageDrawable(icon); + } + } mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); // We can resolve the Gravity here by using the Locale for getting // the layout direction diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index 9fa2c23..aa1c265 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -46,4 +46,12 @@ interface IAppOpsService { void setUserRestrictions(in Bundle restrictions, int userHandle); void removeUser(int userHandle); + boolean isControlAllowed(int code, String packageName); + + // Privacy guard methods + boolean getPrivacyGuardSettingForPackage(int uid, String packageName); + void setPrivacyGuardSettingForPackage(int uid, String packageName, boolean state); + + // AppOps accounting + void resetCounters(); } diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 3cddbf6..ba92f48 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -1,5 +1,6 @@ /* * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2016 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -121,4 +122,18 @@ interface IBatteryStats { void setBatteryState(int status, int health, int plugType, int level, int temp, int volt); long getAwakeTimeBattery(); long getAwakeTimePlugged(); + + + /** @hide */ + byte[] getDockStatistics(); + /** @hide */ + ParcelFileDescriptor getDockStatisticsStream(); + /** @hide **/ + void resetStatistics(); + /** @hide **/ + void setDockBatteryState(int status, int health, int plugType, int level, int temp, int volt); + /** @hide **/ + long getAwakeTimeDockBattery(); + /** @hide **/ + long getAwakeTimeDockPlugged(); } diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java index 4efefa9..83160cd 100644 --- a/core/java/com/android/internal/app/LocalePicker.java +++ b/core/java/com/android/internal/app/LocalePicker.java @@ -1,4 +1,7 @@ /* + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. + * Not a Contribution. + * * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -41,6 +44,7 @@ import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.ArrayList; +import java.util.Arrays; public class LocalePicker extends ListFragment { private static final String TAG = "LocalePicker"; @@ -83,12 +87,23 @@ public class LocalePicker extends ListFragment { } } + public static ArrayList<String> getLocaleArray(String[] locales, Resources resources) { + String locale_codes = resources.getString(R.string.locale_codes); + String[] localeCodesArray = null; + if (locale_codes != null && !"".equals(locale_codes.trim())) { + localeCodesArray = locale_codes.split(","); + } + ArrayList<String> localeList = new ArrayList<String>( + Arrays.asList((localeCodesArray == null || localeCodesArray.length == 0) ? locales + : localeCodesArray)); + return localeList; + } + public static List<LocaleInfo> getAllAssetLocales(Context context, boolean isInDeveloperMode) { final Resources resources = context.getResources(); - final String[] locales = Resources.getSystem().getAssets().getLocales(); - List<String> localeList = new ArrayList<String>(locales.length); - Collections.addAll(localeList, locales); + String[] locales = Resources.getSystem().getAssets().getLocales(); + ArrayList<String> localeList = getLocaleArray(locales, resources); // Don't show the pseudolocales unless we're in developer mode. http://b/17190407. if (!isInDeveloperMode) { diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java index 2595fe0..efe88ff 100644 --- a/core/java/com/android/internal/app/PlatLogoActivity.java +++ b/core/java/com/android/internal/app/PlatLogoActivity.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2014-2015 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -57,10 +58,13 @@ public class PlatLogoActivity extends Activity { int mKeyCount; PathInterpolator mInterpolator = new PathInterpolator(0f, 0f, 0.5f, 1f); + private boolean mIsCM; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + mIsCM = getIntent().hasExtra("is_cm"); mLayout = new FrameLayout(this); setContentView(mLayout); } @@ -153,6 +157,7 @@ public class PlatLogoActivity extends Activity { .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) + .putExtra("is_cm", mIsCM) .addCategory("com.android.internal.category.PLATLOGO")); } catch (ActivityNotFoundException ex) { Log.e("PlatLogoActivity", "No more eggs."); @@ -202,7 +207,9 @@ public class PlatLogoActivity extends Activity { } public void showMarshmallow(View im) { - final Drawable fg = getDrawable(com.android.internal.R.drawable.platlogo); + final Drawable fg = getDrawable(mIsCM + ? com.android.internal.R.drawable.platlogo_cm + : com.android.internal.R.drawable.platlogo); fg.setBounds(0, 0, im.getWidth(), im.getHeight()); fg.setAlpha(0); im.getOverlay().add(fg); diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 4ba678c..4c3cc4d 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -91,8 +91,9 @@ public class ResolverActivity extends Activity { private ResolveListAdapter mAdapter; private PackageManager mPm; private boolean mSafeForwardingMode; - private boolean mAlwaysUseOption; + /*package*/ boolean mAlwaysUseOption; private AbsListView mAdapterView; + private ViewGroup mFilteredItemContainer; private Button mAlwaysButton; private Button mOnceButton; private View mProfileView; @@ -305,6 +306,21 @@ public class ResolverActivity extends Activity { } if (mAdapter.hasFilteredItem()) { + mFilteredItemContainer = (ViewGroup) findViewById(R.id.filtered_item_container); + mFilteredItemContainer.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + DisplayResolveInfo filteredItem = mAdapter.getFilteredItem(); + + if (filteredItem == null) { + return false; + } + + showAppDetails(filteredItem.getResolveInfo()); + return true; + } + }); + setAlwaysButtonEnabled(true, mAdapter.getFilteredPosition(), false); mOnceButton.setEnabled(true); } @@ -1555,7 +1571,7 @@ public class ResolverActivity extends Activity { return mDisplayList.get(index); } - public final View getView(int position, View convertView, ViewGroup parent) { + public View getView(int position, View convertView, ViewGroup parent) { View view = convertView; if (view == null) { view = createView(parent); diff --git a/core/java/com/android/internal/app/ResolverProxy.java b/core/java/com/android/internal/app/ResolverProxy.java new file mode 100644 index 0000000..f59fd11 --- /dev/null +++ b/core/java/com/android/internal/app/ResolverProxy.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2016 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +import java.util.List; +import android.content.pm.ResolveInfo; +import android.content.Context; +import android.content.Intent; +import android.widget.AbsListView; +import android.app.VoiceInteractor.PickOptionRequest.Option; +import com.android.internal.app.ResolverActivity.TargetInfo; + +/** Relax access modifiers on key ResolverActivity extension methods to allow + them to be overridden from a different package/classloader. + Used by CMResolver */ +public class ResolverProxy extends ResolverActivity { + private static final String TAG = "ResolverProxy"; + + /** If the superclass may set up adapter entries after onCreate completes, + This method should be overridden to do nothing, and + sendVoiceChoicesIfNeeded should be called once the adapter setup is + complete. */ + @Override + protected void onSetupVoiceInteraction() { + super.onSetupVoiceInteraction(); + } + + /** see onSetupVoiceInteraction */ + @Override + protected void sendVoiceChoicesIfNeeded() { + super.sendVoiceChoicesIfNeeded(); + } + + @Override + protected int getLayoutResource() { + return super.getLayoutResource(); + } + + @Override + protected void bindProfileView() { + super.bindProfileView(); + } + + @Override + protected Option optionForChooserTarget(TargetInfo target, int index) { + return super.optionForChooserTarget(target, index); + } + + @Override + protected boolean shouldGetActivityMetadata() { + return super.shouldGetActivityMetadata(); + } + + @Override + protected boolean shouldAutoLaunchSingleChoice(TargetInfo target) { + return super.shouldAutoLaunchSingleChoice(target); + } + + @Override + protected void showAppDetails(ResolveInfo ri) { + super.showAppDetails(ri); + } + + @Override + void startSelected(int which, boolean always, boolean filtered) { + super.startSelected(which, always, filtered); + } + + @Override + protected void onActivityStarted(TargetInfo cti) { + super.onActivityStarted(cti); + } + + @Override + protected boolean configureContentView( + List<Intent> payloadIntents, Intent[] initialIntents, + List<ResolveInfo> rList, boolean alwaysUseOption) { + return super.configureContentView( + payloadIntents, initialIntents, rList, alwaysUseOption); + } + + @Override + protected void onPrepareAdapterView( + AbsListView adapterView, ResolveListAdapter adapter, boolean alwaysUseOption) { + super.onPrepareAdapterView(adapterView, adapter, alwaysUseOption); + } + + /** subclasses cannot override this because ResolveListAdapter is an inaccessible + type. Override createProxyAdapter(...) instead */ + @Override + ResolveListAdapter createAdapter(Context context, List<Intent> payloadIntents, + Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid, + boolean filterLastUsed) { + ProxyListAdapter adapter = createProxyAdapter( + context, payloadIntents, initialIntents, rList, launchedFromUid, filterLastUsed); + return (adapter != null) + ? adapter + : super.createAdapter(context, payloadIntents, initialIntents, + rList, launchedFromUid, filterLastUsed); + } + + /** Subclasses should override this instead of createAdapter to avoid issues + with ResolveListAdapter being an inaccessible type */ + protected ProxyListAdapter createProxyAdapter(Context context, List<Intent> payloadIntents, + Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid, + boolean filterLastUsed) { + return null; + } + + protected void setAlwaysUseOption(boolean alwaysUse) { + mAlwaysUseOption = alwaysUse; + } + + /** Provides a visible type for exending ResolveListAdapter - fortunately the key + methods one would need to override in ResolveListAdapter are all public or protected */ + public class ProxyListAdapter extends ResolveListAdapter { + public ProxyListAdapter( + Context context, List<Intent> payloadIntents, Intent[] initialIntents, + List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) { + super(context, payloadIntents, initialIntents, rList, launchedFromUid, filterLastUsed); + } + + /** complements getDisplayInfoCount and getDisplayInfoAt */ + public TargetInfo removeDisplayInfoAt(int index) { + if (index >= 0 && index < mDisplayList.size()) { + return mDisplayList.remove(index); + } else { + return null; + } + } + } +} diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java index f479f4f..f5b948f 100644 --- a/core/java/com/android/internal/content/NativeLibraryHelper.java +++ b/core/java/com/android/internal/content/NativeLibraryHelper.java @@ -62,6 +62,8 @@ public class NativeLibraryHelper { // that the cpuAbiOverride must be clear. public static final String CLEAR_ABI_OVERRIDE = "-"; + private static final Object mRestoreconSync = new Object(); + /** * A handle to an opened package, consisting of one or more APKs. Used as * input to the various NativeLibraryHelper methods. Allows us to scan and @@ -275,8 +277,12 @@ public class NativeLibraryHelper { throw new IOException("Cannot chmod native library directory " + path.getPath(), e); } - } else if (!SELinux.restorecon(path)) { - throw new IOException("Cannot set SELinux context for " + path.getPath()); + } else { + synchronized (mRestoreconSync) { + if (!SELinux.restorecon(path)) { + throw new IOException("Cannot set SELinux context for " + path.getPath()); + } + } } } diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java index f178c8c..54a4e86 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -21,6 +21,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.hardware.SensorManager; import android.net.ConnectivityManager; +import android.os.BatteryManager; import android.os.BatteryStats; import android.os.BatteryStats.Uid; import android.os.Bundle; @@ -61,15 +62,18 @@ public final class BatteryStatsHelper { private static final String TAG = BatteryStatsHelper.class.getSimpleName(); private static BatteryStats sStatsXfer; + private static BatteryStats sDockStatsXfer; private static Intent sBatteryBroadcastXfer; private static ArrayMap<File, BatteryStats> sFileXfer = new ArrayMap<>(); final private Context mContext; + final private BatteryManager mBatteryService; final private boolean mCollectBatteryBroadcast; final private boolean mWifiOnly; private IBatteryStats mBatteryInfo; private BatteryStats mStats; + private BatteryStats mDockStats; private Intent mBatteryBroadcast; private PowerProfile mPowerProfile; @@ -160,19 +164,28 @@ public final class BatteryStatsHelper { public BatteryStatsHelper(Context context, boolean collectBatteryBroadcast, boolean wifiOnly) { mContext = context; + mBatteryService = ((BatteryManager) context.getSystemService(Context.BATTERY_SERVICE)); mCollectBatteryBroadcast = collectBatteryBroadcast; mWifiOnly = wifiOnly; } public void storeStatsHistoryInFile(String fname) { + internalStoreStatsHistoryInFile(getStats(), fname); + } + + public void storeDockStatsHistoryInFile(String fname) { + internalStoreStatsHistoryInFile(getDockStats(), fname); + } + + public void internalStoreStatsHistoryInFile(BatteryStats stats, String fname) { synchronized (sFileXfer) { File path = makeFilePath(mContext, fname); - sFileXfer.put(path, this.getStats()); + sFileXfer.put(path, stats); FileOutputStream fout = null; try { fout = new FileOutputStream(path); Parcel hist = Parcel.obtain(); - getStats().writeToParcelWithoutUids(hist, 0); + stats.writeToParcelWithoutUids(hist, 0); byte[] histData = hist.marshall(); fout.write(histData); } catch (IOException e) { @@ -229,18 +242,38 @@ public final class BatteryStatsHelper { /** Clears the current stats and forces recreating for future use. */ public void clearStats() { mStats = null; + mDockStats = null; + } + + private void clearAllStats() { + clearStats(); + sStatsXfer = null; + sDockStatsXfer = null; + sBatteryBroadcastXfer = null; + for (File f : sFileXfer.keySet()) { + f.delete(); + } + sFileXfer.clear(); } public BatteryStats getStats() { if (mStats == null) { - load(); + loadStats(); } return mStats; } + public BatteryStats getDockStats() { + if (mDockStats == null) { + loadDockStats(); + } + return mDockStats; + } + public Intent getBatteryBroadcast() { if (mBatteryBroadcast == null && mCollectBatteryBroadcast) { - load(); + loadStats(); + loadDockStats(); } return mBatteryBroadcast; } @@ -257,6 +290,7 @@ public final class BatteryStatsHelper { public void create(Bundle icicle) { if (icicle != null) { mStats = sStatsXfer; + mDockStats = sDockStatsXfer; mBatteryBroadcast = sBatteryBroadcastXfer; } mBatteryInfo = IBatteryStats.Stub.asInterface( @@ -266,6 +300,7 @@ public final class BatteryStatsHelper { public void storeState() { sStatsXfer = mStats; + sDockStatsXfer = mDockStats; sBatteryBroadcastXfer = mBatteryBroadcast; } @@ -321,6 +356,7 @@ public final class BatteryStatsHelper { long rawUptimeUs) { // Initialize mStats if necessary. getStats(); + getDockStats(); mMaxPower = 0; mMaxRealPower = 0; @@ -739,7 +775,7 @@ public final class BatteryStatsHelper { } } - private void load() { + private void loadStats() { if (mBatteryInfo == null) { return; } @@ -750,6 +786,26 @@ public final class BatteryStatsHelper { } } + private void loadDockStats() { + if (mBatteryInfo == null) { + return; + } + if (mBatteryService.isDockBatterySupported()) { + mDockStats = getDockStats(mBatteryInfo); + } else { + mDockStats = null; + } + } + + public void resetStatistics() { + try { + clearAllStats(); + mBatteryInfo.resetStatistics(); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException:", e); + } + } + private static BatteryStatsImpl getStats(IBatteryStats service) { try { ParcelFileDescriptor pfd = service.getStatisticsStream(); @@ -772,4 +828,27 @@ public final class BatteryStatsHelper { } return new BatteryStatsImpl(); } + + private static BatteryStatsImpl getDockStats(IBatteryStats service) { + try { + ParcelFileDescriptor pfd = service.getDockStatisticsStream(); + if (pfd != null) { + FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd); + try { + byte[] data = readFully(fis, MemoryFile.getSize(pfd.getFileDescriptor())); + Parcel parcel = Parcel.obtain(); + parcel.unmarshall(data, 0, data.length); + parcel.setDataPosition(0); + BatteryStatsImpl stats = com.android.internal.os.DockBatteryStatsImpl.CREATOR + .createFromParcel(parcel); + return stats; + } catch (IOException e) { + Log.w(TAG, "Unable to read statistics stream", e); + } + } + } catch (RemoteException e) { + Log.w(TAG, "RemoteException:", e); + } + return new BatteryStatsImpl(); + } } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 64b7768..d0a169e 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2006-2007 The Android Open Source Project + * Copyright (C) 2016 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -91,7 +92,7 @@ import java.util.concurrent.locks.ReentrantLock; * battery life. All times are represented in microseconds except where indicated * otherwise. */ -public final class BatteryStatsImpl extends BatteryStats { +public class BatteryStatsImpl extends BatteryStats { private static final String TAG = "BatteryStatsImpl"; private static final boolean DEBUG = false; public static final boolean DEBUG_ENERGY = false; @@ -105,7 +106,7 @@ public final class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 132 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 133 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS = 2000; @@ -1968,8 +1969,14 @@ public final class BatteryStatsImpl extends BatteryStats { private int buildBatteryLevelInt(HistoryItem h) { return ((((int)h.batteryLevel)<<25)&0xfe000000) - | ((((int)h.batteryTemperature)<<14)&0x01ff8000) - | ((((int)h.batteryVoltage)<<1)&0x00007fff); + | ((((int)h.batteryTemperature)<<15)&0x01ff8000) + | ((((int)h.batteryVoltage)<<1)&0x00007ffe); + } + + private void readBatteryLevelInt(int batteryLevelInt, HistoryItem out) { + out.batteryLevel = (byte)((batteryLevelInt & 0xfe000000) >>> 25); + out.batteryTemperature = (short)((batteryLevelInt & 0x01ff8000) >>> 15); + out.batteryVoltage = (char)((batteryLevelInt & 0x00007ffe) >>> 1); } private int buildStateInt(HistoryItem h) { @@ -2110,9 +2117,7 @@ public final class BatteryStatsImpl extends BatteryStats { final int batteryLevelInt; if ((firstToken&DELTA_BATTERY_LEVEL_FLAG) != 0) { batteryLevelInt = src.readInt(); - cur.batteryLevel = (byte)((batteryLevelInt>>25)&0x7f); - cur.batteryTemperature = (short)((batteryLevelInt<<7)>>21); - cur.batteryVoltage = (char)(batteryLevelInt&0x3fff); + readBatteryLevelInt(batteryLevelInt, cur); cur.numReadInts += 1; if (DEBUG) Slog.i(TAG, "READ DELTA: batteryToken=0x" + Integer.toHexString(batteryLevelInt) @@ -6839,13 +6844,13 @@ public final class BatteryStatsImpl extends BatteryStats { public BatteryStatsImpl(File systemDir, Handler handler, ExternalStatsSync externalSync) { if (systemDir != null) { - mFile = new JournaledFile(new File(systemDir, "batterystats.bin"), - new File(systemDir, "batterystats.bin.tmp")); + mFile = new JournaledFile(new File(systemDir, getStatsName() + ".bin"), + new File(systemDir, getStatsName() + ".bin.tmp")); } else { mFile = null; } - mCheckinFile = new AtomicFile(new File(systemDir, "batterystats-checkin.bin")); - mDailyFile = new AtomicFile(new File(systemDir, "batterystats-daily.xml")); + mCheckinFile = new AtomicFile(new File(systemDir, getStatsName() + "-checkin.bin")); + mDailyFile = new AtomicFile(new File(systemDir, getStatsName () + "-daily.xml")); mExternalSync = externalSync; mHandler = new MyHandler(handler.getLooper()); mStartCount++; @@ -6921,6 +6926,16 @@ public final class BatteryStatsImpl extends BatteryStats { readFromParcel(p); } + /** @hide */ + protected String getStatsName() { + return "batterystats"; + } + + /** @hide */ + protected String getLogName() { + return "BatteryStats"; + } + public void setPowerProfile(PowerProfile profile) { synchronized (this) { mPowerProfile = profile; @@ -7699,26 +7714,35 @@ public final class BatteryStatsImpl extends BatteryStats { } final Uid u = getUidStatsLocked(mapUid(entry.uid)); - u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.rxBytes, - entry.rxPackets); - u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.txBytes, - entry.txPackets); - rxPackets.put(u.getUid(), entry.rxPackets); - txPackets.put(u.getUid(), entry.txPackets); + if (entry.rxBytes != 0) { + u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.rxBytes, + entry.rxPackets); + mNetworkByteActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked( + entry.rxBytes); + mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked( + entry.rxPackets); - // Sum the total number of packets so that the Rx Power and Tx Power can - // be evenly distributed amongst the apps. - totalRxPackets += entry.rxPackets; - totalTxPackets += entry.txPackets; + rxPackets.put(u.getUid(), entry.rxPackets); - mNetworkByteActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked( - entry.rxBytes); - mNetworkByteActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked( - entry.txBytes); - mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked( - entry.rxPackets); - mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked( - entry.txPackets); + // Sum the total number of packets so that the Rx Power can + // be evenly distributed amongst the apps. + totalRxPackets += entry.rxPackets; + } + + if (entry.txBytes != 0) { + u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.txBytes, + entry.txPackets); + mNetworkByteActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked( + entry.txBytes); + mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked( + entry.txPackets); + + txPackets.put(u.getUid(), entry.txPackets); + + // Sum the total number of packets so that the Tx Power can + // be evenly distributed amongst the apps. + totalTxPackets += entry.txPackets; + } } } @@ -8434,7 +8458,13 @@ public final class BatteryStatsImpl extends BatteryStats { public void setBatteryStateLocked(int status, int health, int plugType, int level, int temp, int volt) { - final boolean onBattery = plugType == BATTERY_PLUGGED_NONE; + // We need to add a extra check over the status because of dock batteries + // PlugType doesn't means that the dock battery is charging (some devices + // doesn't charge under dock usb) + boolean onBattery = plugType == BATTERY_PLUGGED_NONE && + (status != BatteryManager.BATTERY_STATUS_CHARGING || + status != BatteryManager.BATTERY_STATUS_FULL); + final long uptime = SystemClock.uptimeMillis(); final long elapsedRealtime = SystemClock.elapsedRealtime(); if (!mHaveBatteryLevel) { diff --git a/core/java/com/android/internal/os/DeviceKeyHandler.java b/core/java/com/android/internal/os/DeviceKeyHandler.java new file mode 100644 index 0000000..e7d103d --- /dev/null +++ b/core/java/com/android/internal/os/DeviceKeyHandler.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2012 The CyanogenMod Project Licensed under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law + * or agreed to in writing, software distributed under the License is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +package com.android.internal.os; + +import android.view.KeyEvent; + +public interface DeviceKeyHandler { + + /** + * Invoked when an unknown key was detected by the system, letting the device handle + * this special keys prior to pass the key to the active app. + * + * @param event The key event to be handled + * @return If the event is consume + */ + public boolean handleKeyEvent(KeyEvent event); +} diff --git a/core/java/com/android/internal/os/DockBatteryStatsImpl.java b/core/java/com/android/internal/os/DockBatteryStatsImpl.java new file mode 100644 index 0000000..6099ad2 --- /dev/null +++ b/core/java/com/android/internal/os/DockBatteryStatsImpl.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +import android.os.Handler; +import android.os.Parcel; +import android.os.Parcelable; + +import java.io.File; + +public final class DockBatteryStatsImpl extends BatteryStatsImpl { + public DockBatteryStatsImpl() { + super(); + } + + public DockBatteryStatsImpl(Parcel p) { + super(p); + } + + public DockBatteryStatsImpl(File systemDir, Handler handler, ExternalStatsSync externalSync) { + super(systemDir, handler, externalSync); + } + + protected String getStatsName() { + return "dockbatterystats"; + } + + protected String getLogName() { + return "DockBatteryStats"; + } + + public static final Parcelable.Creator<DockBatteryStatsImpl> CREATOR = + new Parcelable.Creator<DockBatteryStatsImpl>() { + public DockBatteryStatsImpl createFromParcel(Parcel in) { + return new DockBatteryStatsImpl(in); + } + + public DockBatteryStatsImpl[] newArray(int size) { + return new DockBatteryStatsImpl[size]; + } + }; +} diff --git a/core/java/com/android/internal/os/KernelCpuSpeedReader.java b/core/java/com/android/internal/os/KernelCpuSpeedReader.java index 5b776ac..3f6ebb9 100644 --- a/core/java/com/android/internal/os/KernelCpuSpeedReader.java +++ b/core/java/com/android/internal/os/KernelCpuSpeedReader.java @@ -16,8 +16,11 @@ package com.android.internal.os; import android.text.TextUtils; +import android.system.OsConstants; import android.util.Slog; +import libcore.io.Libcore; + import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; @@ -29,7 +32,7 @@ import java.util.Arrays; * * freq time * - * where time is measured in 1/100 seconds. + * where time is measured in jiffies. */ public class KernelCpuSpeedReader { private static final String TAG = "KernelCpuSpeedReader"; @@ -38,6 +41,9 @@ public class KernelCpuSpeedReader { private final long[] mLastSpeedTimes; private final long[] mDeltaSpeedTimes; + // How long a CPU jiffy is in milliseconds. + private final long mJiffyMillis; + /** * @param cpuNumber The cpu (cpu0, cpu1, etc) whose state to read. */ @@ -46,6 +52,8 @@ public class KernelCpuSpeedReader { cpuNumber); mLastSpeedTimes = new long[numSpeedSteps]; mDeltaSpeedTimes = new long[numSpeedSteps]; + long jiffyHz = Libcore.os.sysconf(OsConstants._SC_CLK_TCK); + mJiffyMillis = 1000/jiffyHz; } /** @@ -62,8 +70,7 @@ public class KernelCpuSpeedReader { splitter.setString(line); Long.parseLong(splitter.next()); - // The proc file reports time in 1/100 sec, so convert to milliseconds. - long time = Long.parseLong(splitter.next()) * 10; + long time = Long.parseLong(splitter.next()) * mJiffyMillis; if (time < mLastSpeedTimes[speedIndex]) { // The stats reset when the cpu hotplugged. That means that the time // we read is offset from 0, so the time is the delta. diff --git a/core/java/com/android/internal/os/ProcessCpuTracker.java b/core/java/com/android/internal/os/ProcessCpuTracker.java index bf97f1f..d831902 100644 --- a/core/java/com/android/internal/os/ProcessCpuTracker.java +++ b/core/java/com/android/internal/os/ProcessCpuTracker.java @@ -67,10 +67,10 @@ public class ProcessCpuTracker { static final int PROCESS_STAT_UTIME = 2; static final int PROCESS_STAT_STIME = 3; - /** Stores user time and system time in 100ths of a second. */ + /** Stores user time and system time in jiffies. */ private final long[] mProcessStatsData = new long[4]; - /** Stores user time and system time in 100ths of a second. Used for + /** Stores user time and system time in jiffies. Used for * public API to retrieve CPU use for a process. Must lock while in use. */ private final long[] mSinglePidStatsData = new long[4]; diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index f81658e..3377189 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -19,6 +19,7 @@ package com.android.internal.os; import android.app.ActivityManagerNative; import android.app.ActivityThread; import android.app.ApplicationErrorReport; +import android.content.res.ThemeConfig; import android.os.Build; import android.os.Debug; import android.os.IBinder; @@ -83,6 +84,10 @@ public class RuntimeInit { message.append("Process: ").append(processName).append(", "); } message.append("PID: ").append(Process.myPid()); + final ThemeConfig themeConfig = + ActivityManagerNative.getDefault().getConfiguration().themeConfig; + message.append("\nTheme: ").append(themeConfig == null ? + ThemeConfig.SYSTEM_DEFAULT : themeConfig); Clog_e(TAG, message.toString(), e); } diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index 3e86fac..6c3cb3e 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -22,6 +22,7 @@ import static android.system.OsConstants.STDERR_FILENO; import static android.system.OsConstants.STDIN_FILENO; import static android.system.OsConstants.STDOUT_FILENO; +import android.graphics.Typeface; import android.net.Credentials; import android.net.LocalSocket; import android.os.Process; @@ -194,6 +195,10 @@ class ZygoteConnection { Os.fcntlInt(childPipeFd, F_SETFD, 0); } + if (parsedArgs.refreshTheme) { + Typeface.recreateDefaults(); + } + /** * In order to avoid leaking descriptors to the Zygote child, * the native code must close the two Zygote socket descriptors @@ -373,6 +378,9 @@ class ZygoteConnection { */ String appDataDir; + /** from --refresh_theme */ + boolean refreshTheme; + /** * Constructs instance and parses args * @param args zygote command-line args @@ -529,6 +537,8 @@ class ZygoteConnection { instructionSet = arg.substring(arg.indexOf('=') + 1); } else if (arg.startsWith("--app-data-dir=")) { appDataDir = arg.substring(arg.indexOf('=') + 1); + } else if (arg.equals("--refresh_theme")) { + refreshTheme = true; } else { break; } diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl index e330de2..6f47f70 100644 --- a/core/java/com/android/internal/policy/IKeyguardService.aidl +++ b/core/java/com/android/internal/policy/IKeyguardService.aidl @@ -94,4 +94,5 @@ oneway interface IKeyguardService { * to start the keyguard dismiss sequence. */ void onActivityDrawn(); + void showKeyguard(); } diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 8e8d352..fe0bc65 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -76,12 +76,19 @@ import com.android.internal.widget.SwipeDismissLayout; import android.app.ActivityManager; import android.app.KeyguardManager; +import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; import android.content.res.Resources.Theme; import android.content.res.TypedArray; +import android.content.ActivityNotFoundException; +import android.database.ContentObserver; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.PixelFormat; @@ -93,10 +100,14 @@ import android.media.session.MediaSessionLegacyHelper; import android.net.Uri; import android.os.Bundle; import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; +import android.provider.Settings; import android.transition.Scene; import android.transition.Transition; import android.transition.TransitionInflater; @@ -108,6 +119,8 @@ import android.util.EventLog; import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; +import android.view.GestureDetector; +import android.view.GestureDetector.SimpleOnGestureListener; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.animation.Animation; @@ -118,10 +131,14 @@ import android.widget.ImageView; import android.widget.PopupWindow; import android.widget.ProgressBar; import android.widget.TextView; +import android.widget.Toast; import java.lang.ref.WeakReference; import java.util.ArrayList; +import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.R; + /** * Android-specific Window. * <p> @@ -249,7 +266,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private int mTitleColor = 0; private boolean mAlwaysReadCloseOnTouchAttr = false; + private boolean mEnableGestures; + private Context mContext; private ContextMenuBuilder mContextMenu; private MenuDialogHelper mContextMenuHelper; private boolean mClosingActionMenu; @@ -260,6 +279,36 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private AudioManager mAudioManager; private KeyguardManager mKeyguardManager; + private final class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(Settings.System + .getUriFor(Settings.System.ENABLE_STYLUS_GESTURES), false, + this); + checkGestures(); + } + + void unobserve() { + ContentResolver resolver = mContext.getContentResolver(); + resolver.unregisterContentObserver(this); + } + + @Override + public void onChange(boolean selfChange) { + checkGestures(); + } + + void checkGestures() { + mEnableGestures = Settings.System.getInt( + mContext.getContentResolver(), + Settings.System.ENABLE_STYLUS_GESTURES, 0) == 1; + } + } + private int mUiOptions = 0; private boolean mInvalidatePanelMenuPosted; @@ -303,6 +352,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { public PhoneWindow(Context context) { super(context); + mContext = context; mLayoutInflater = LayoutInflater.from(context); } @@ -2224,6 +2274,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private View mStatusGuard; // View added at runtime to draw under the navigation bar area private View mNavigationGuard; + private SettingsObserver mSettingsObserver; private final ColorViewState mStatusColorViewState = new ColorViewState( SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS, @@ -2267,6 +2318,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { mBarEnterExitDuration = context.getResources().getInteger( R.integer.dock_enter_exit_duration); + + mSettingsObserver = new SettingsObserver(new Handler()); } public void setBackgroundFallback(int resId) { @@ -2358,8 +2411,211 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return false; } + private final StylusGestureFilter mStylusFilter = new StylusGestureFilter(); + + private class StylusGestureFilter extends SimpleOnGestureListener { + + private final static int SWIPE_UP = 1; + private final static int SWIPE_DOWN = 2; + private final static int SWIPE_LEFT = 3; + private final static int SWIPE_RIGHT = 4; + private final static int PRESS_LONG = 5; + private final static int TAP_DOUBLE = 6; + private final static double SWIPE_MIN_DISTANCE = 25.0; + private final static double SWIPE_MIN_VELOCITY = 50.0; + private final static int KEY_NO_ACTION = 1000; + private final static int KEY_HOME = 1001; + private final static int KEY_BACK = 1002; + private final static int KEY_MENU = 1003; + private final static int KEY_SEARCH = 1004; + private final static int KEY_RECENT = 1005; + private final static int KEY_APP = 1006; + private GestureDetector mDetector; + private final static String TAG = "StylusGestureFilter"; + + public StylusGestureFilter() { + mDetector = new GestureDetector(this); + } + + public boolean onTouchEvent(MotionEvent event) { + return mDetector.onTouchEvent(event); + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, + float velocityX, float velocityY) { + + final float xDistance = Math.abs(e1.getX() - e2.getX()); + final float yDistance = Math.abs(e1.getY() - e2.getY()); + + velocityX = Math.abs(velocityX); + velocityY = Math.abs(velocityY); + boolean result = false; + + if (velocityX > (SWIPE_MIN_VELOCITY * getResources().getDisplayMetrics().density) + && xDistance > (SWIPE_MIN_DISTANCE * getResources().getDisplayMetrics().density) + && xDistance > yDistance) { + if (e1.getX() > e2.getX()) { // right to left + // Swipe Left + dispatchStylusAction(SWIPE_LEFT); + } else { + // Swipe Right + dispatchStylusAction(SWIPE_RIGHT); + } + result = true; + } else if (velocityY > (SWIPE_MIN_VELOCITY * getResources().getDisplayMetrics().density) + && yDistance > (SWIPE_MIN_DISTANCE * getResources().getDisplayMetrics().density) + && yDistance > xDistance) { + if (e1.getY() > e2.getY()) { // bottom to up + // Swipe Up + dispatchStylusAction(SWIPE_UP); + } else { + // Swipe Down + dispatchStylusAction(SWIPE_DOWN); + } + result = true; + } + return result; + } + + @Override + public boolean onDoubleTap(MotionEvent arg0) { + dispatchStylusAction(TAP_DOUBLE); + return true; + } + + public void onLongPress(MotionEvent e) { + dispatchStylusAction(PRESS_LONG); + } + + } + + private void menuAction() { + dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, + KeyEvent.KEYCODE_MENU)); + dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, + KeyEvent.KEYCODE_MENU)); + + } + + private void backAction() { + dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, + KeyEvent.KEYCODE_BACK)); + dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, + KeyEvent.KEYCODE_BACK)); + } + + private void dispatchStylusAction(int gestureAction) { + final ContentResolver resolver = mContext.getContentResolver(); + boolean isSystemUI = mContext.getPackageName().equals("com.android.systemui"); + String setting = null; + int dispatchAction = -1; + switch (gestureAction) { + case StylusGestureFilter.SWIPE_LEFT: + setting = Settings.System.getString(resolver, + Settings.System.GESTURES_LEFT_SWIPE); + break; + case StylusGestureFilter.SWIPE_RIGHT: + setting = Settings.System.getString(resolver, + Settings.System.GESTURES_RIGHT_SWIPE); + break; + case StylusGestureFilter.SWIPE_UP: + setting = Settings.System.getString(resolver, + Settings.System.GESTURES_UP_SWIPE); + break; + case StylusGestureFilter.SWIPE_DOWN: + setting = Settings.System.getString(resolver, + Settings.System.GESTURES_DOWN_SWIPE); + break; + case StylusGestureFilter.TAP_DOUBLE: + setting = Settings.System.getString(resolver, + Settings.System.GESTURES_DOUBLE_TAP); + break; + case StylusGestureFilter.PRESS_LONG: + setting = Settings.System.getString(resolver, + Settings.System.GESTURES_LONG_PRESS); + break; + default: + return; + } + + try { + int value = Integer.valueOf(setting); + if (value == StylusGestureFilter.KEY_NO_ACTION) { + return; + } + dispatchAction = value; + } catch (NumberFormatException e) { + dispatchAction = StylusGestureFilter.KEY_APP; + } + + // Dispatching action + switch (dispatchAction) { + case StylusGestureFilter.KEY_HOME: + Intent homeIntent = new Intent(Intent.ACTION_MAIN); + homeIntent.addCategory(Intent.CATEGORY_HOME); + homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivity(homeIntent); + break; + case StylusGestureFilter.KEY_BACK: + backAction(); + break; + case StylusGestureFilter.KEY_MENU: + // Menu action on notificationbar / systemui will be converted + // to back action + if (isSystemUI) { + backAction(); + break; + } + menuAction(); + break; + case StylusGestureFilter.KEY_SEARCH: + // Search action on notificationbar / systemui will be converted + // to back action + if (isSystemUI) { + backAction(); + break; + } + launchDefaultSearch(new KeyEvent(KeyEvent.ACTION_UP, + KeyEvent.KEYCODE_SEARCH)); + break; + case StylusGestureFilter.KEY_RECENT: + IStatusBarService mStatusBarService = IStatusBarService.Stub + .asInterface(ServiceManager.getService("statusbar")); + try { + mStatusBarService.toggleRecentApps(); + } catch (RemoteException e) { + } + break; + case StylusGestureFilter.KEY_APP: + // Launching app on notificationbar / systemui will be preceded + // with a back Action + if (isSystemUI) { + backAction(); + } + try { + final PackageManager pm = mContext.getPackageManager(); + Intent launchIntent = pm.getLaunchIntentForPackage(setting); + if (launchIntent != null) { + mContext.startActivity(launchIntent); + } + } catch (ActivityNotFoundException e) { + Toast.makeText(mContext, mContext.getString(R.string.stylus_app_not_installed, setting), + Toast.LENGTH_LONG).show(); + } + break; + } + } + @Override public boolean dispatchTouchEvent(MotionEvent ev) { + // Stylus events with side button pressed are filtered and other + // events are processed normally. + if (mEnableGestures + && MotionEvent.BUTTON_SECONDARY == ev.getButtonState()) { + mStylusFilter.onTouchEvent(ev); + return false; + } final Callback cb = getCallback(); return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev); @@ -3255,6 +3511,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { protected void onAttachedToWindow() { super.onAttachedToWindow(); + mSettingsObserver.observe(); + updateWindowResizeState(); final Callback cb = getCallback(); @@ -3278,6 +3536,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { protected void onDetachedFromWindow() { super.onDetachedFromWindow(); + mSettingsObserver.unobserve(); + final Callback cb = getCallback(); if (cb != null && mFeatureId < 0) { cb.onDetachedFromWindow(); diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java index 447292c..be78a12 100644 --- a/core/java/com/android/internal/util/StateMachine.java +++ b/core/java/com/android/internal/util/StateMachine.java @@ -1879,6 +1879,33 @@ public class StateMachine { } /** + * Check if there are any pending messages with code 'what' in deferred messages queue. + */ + protected final boolean hasDeferredMessages(int what) { + SmHandler smh = mSmHandler; + if (smh == null) return false; + + Iterator<Message> iterator = smh.mDeferredMessages.iterator(); + while (iterator.hasNext()) { + Message msg = iterator.next(); + if (msg.what == what) return true; + } + + return false; + } + + /** + * Check if there are any pending posts of messages with code 'what' in + * the message queue. This does NOT check messages in deferred message queue. + */ + protected final boolean hasMessages(int what) { + SmHandler smh = mSmHandler; + if (smh == null) return false; + + return smh.hasMessages(what); + } + + /** * Validate that the message was sent by * {@link StateMachine#quit} or {@link StateMachine#quitNow}. * */ diff --git a/core/java/com/android/internal/util/cm/ActionUtils.java b/core/java/com/android/internal/util/cm/ActionUtils.java new file mode 100644 index 0000000..7e86cd3 --- /dev/null +++ b/core/java/com/android/internal/util/cm/ActionUtils.java @@ -0,0 +1,149 @@ +package com.android.internal.util.cm; + +import android.app.ActivityManager; +import android.app.ActivityManagerNative; +import android.app.ActivityOptions; +import android.app.IActivityManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Process; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.Log; + +import android.view.HapticFeedbackConstants; +import android.widget.Toast; +import com.android.internal.R; + +import java.util.List; + +public class ActionUtils { + private static final boolean DEBUG = false; + private static final String TAG = ActionUtils.class.getSimpleName(); + private static final String SYSTEMUI_PACKAGE = "com.android.systemui"; + + /** + * Kills the top most / most recent user application, but leaves out the launcher. + * This is function governed by {@link CMSettings.Secure.KILL_APP_LONGPRESS_BACK}. + * + * @param context the current context, used to retrieve the package manager. + * @param userId the ID of the currently active user + * @return {@code true} when a user application was found and closed. + */ + public static boolean killForegroundApp(Context context, int userId) { + try { + return killForegroundAppInternal(context, userId); + } catch (RemoteException e) { + Log.e(TAG, "Could not kill foreground app"); + } + return false; + } + + private static boolean killForegroundAppInternal(Context context, int userId) + throws RemoteException { + try { + final Intent intent = new Intent(Intent.ACTION_MAIN); + String defaultHomePackage = "com.android.launcher"; + intent.addCategory(Intent.CATEGORY_HOME); + final ResolveInfo res = context.getPackageManager().resolveActivity(intent, 0); + + if (res.activityInfo != null && !res.activityInfo.packageName.equals("android")) { + defaultHomePackage = res.activityInfo.packageName; + } + + IActivityManager am = ActivityManagerNative.getDefault(); + List<ActivityManager.RunningAppProcessInfo> apps = am.getRunningAppProcesses(); + for (ActivityManager.RunningAppProcessInfo appInfo : apps) { + int uid = appInfo.uid; + // Make sure it's a foreground user application (not system, + // root, phone, etc.) + if (uid >= Process.FIRST_APPLICATION_UID && uid <= Process.LAST_APPLICATION_UID + && appInfo.importance == + ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { + if (appInfo.pkgList != null && (appInfo.pkgList.length > 0)) { + for (String pkg : appInfo.pkgList) { + if (!pkg.equals("com.android.systemui") + && !pkg.equals(defaultHomePackage)) { + am.forceStopPackage(pkg, UserHandle.USER_CURRENT); + return true; + } + } + } else { + Process.killProcess(appInfo.pid); + return true; + } + } + } + } catch (RemoteException remoteException) { + // Do nothing; just let it go. + } + return false; + } + + /** + * Attempt to bring up the last activity in the stack before the current active one. + * + * @param context + * @return whether an activity was found to switch to + */ + public static boolean switchToLastApp(Context context, int userId) { + try { + return switchToLastAppInternal(context, userId); + } catch (RemoteException e) { + Log.e(TAG, "Could not switch to last app"); + } + return false; + } + + private static boolean switchToLastAppInternal(Context context, int userId) + throws RemoteException { + ActivityManager.RecentTaskInfo lastTask = getLastTask(context, userId); + + if (lastTask == null || lastTask.id < 0) { + return false; + } + + final String packageName = lastTask.baseIntent.getComponent().getPackageName(); + final IActivityManager am = ActivityManagerNative.getDefault(); + final ActivityOptions opts = ActivityOptions.makeCustomAnimation(context, + com.android.internal.R.anim.last_app_in, + com.android.internal.R.anim.last_app_out); + + if (DEBUG) Log.d(TAG, "switching to " + packageName); + am.moveTaskToFront(lastTask.id, ActivityManager.MOVE_TASK_NO_USER_ACTION, opts.toBundle()); + + return true; + } + + private static ActivityManager.RecentTaskInfo getLastTask(Context context, int userId) + throws RemoteException { + final String defaultHomePackage = resolveCurrentLauncherPackage(context, userId); + final IActivityManager am = ActivityManagerNative.getDefault(); + final List<ActivityManager.RecentTaskInfo> tasks = am.getRecentTasks(5, + ActivityManager.RECENT_IGNORE_UNAVAILABLE, userId); + + for (int i = 1; i < tasks.size(); i++) { + ActivityManager.RecentTaskInfo task = tasks.get(i); + if (task.origActivity != null) { + task.baseIntent.setComponent(task.origActivity); + } + String packageName = task.baseIntent.getComponent().getPackageName(); + if (!packageName.equals(defaultHomePackage) + && !packageName.equals(SYSTEMUI_PACKAGE)) { + return tasks.get(i); + } + } + + return null; + } + + private static String resolveCurrentLauncherPackage(Context context, int userId) { + final Intent launcherIntent = new Intent(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_HOME); + final PackageManager pm = context.getPackageManager(); + final ResolveInfo launcherInfo = pm.resolveActivityAsUser(launcherIntent, 0, userId); + return launcherInfo.activityInfo.packageName; + } +} diff --git a/core/java/com/android/internal/util/cm/PowerMenuConstants.java b/core/java/com/android/internal/util/cm/PowerMenuConstants.java new file mode 100644 index 0000000..3debd09 --- /dev/null +++ b/core/java/com/android/internal/util/cm/PowerMenuConstants.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util.cm; + +/* Master list of all actions for the power menu */ +public class PowerMenuConstants { + public static final String GLOBAL_ACTION_KEY_POWER = "power"; + public static final String GLOBAL_ACTION_KEY_REBOOT = "reboot"; + public static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot"; + public static final String GLOBAL_ACTION_KEY_AIRPLANE = "airplane"; + public static final String GLOBAL_ACTION_KEY_USERS = "users"; + public static final String GLOBAL_ACTION_KEY_SETTINGS = "settings"; + public static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown"; + public static final String GLOBAL_ACTION_KEY_BUGREPORT = "bugreport"; + public static final String GLOBAL_ACTION_KEY_SILENT = "silent"; + public static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist"; + public static final String GLOBAL_ACTION_KEY_ASSIST = "assist"; + + private static String[] ALL_ACTIONS = { + GLOBAL_ACTION_KEY_POWER, + GLOBAL_ACTION_KEY_REBOOT, + GLOBAL_ACTION_KEY_SCREENSHOT, + GLOBAL_ACTION_KEY_AIRPLANE, + GLOBAL_ACTION_KEY_USERS, + GLOBAL_ACTION_KEY_SETTINGS, + GLOBAL_ACTION_KEY_LOCKDOWN, + GLOBAL_ACTION_KEY_BUGREPORT, + GLOBAL_ACTION_KEY_SILENT, + GLOBAL_ACTION_KEY_VOICEASSIST, + GLOBAL_ACTION_KEY_ASSIST + }; + + public static String[] getAllActions() { + return ALL_ACTIONS; + } +} diff --git a/core/java/com/android/internal/util/cm/SpamFilter.java b/core/java/com/android/internal/util/cm/SpamFilter.java new file mode 100644 index 0000000..c261009 --- /dev/null +++ b/core/java/com/android/internal/util/cm/SpamFilter.java @@ -0,0 +1,82 @@ +package com.android.internal.util.cm; + +import android.app.Notification; +import android.content.ContentResolver; +import android.net.Uri; +import android.os.Bundle; +import android.text.TextUtils; + +public class SpamFilter { + + public static final String AUTHORITY = "com.cyanogenmod.spam"; + public static final String MESSAGE_PATH = "message"; + public static final Uri NOTIFICATION_URI = new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(AUTHORITY) + .appendEncodedPath(MESSAGE_PATH) + .build(); + + public static final class SpamContract { + + public static final class PackageTable { + public static final String TABLE_NAME = "packages"; + public static final String ID = "_id"; + public static final String PACKAGE_NAME = "package_name"; + } + + public static final class NotificationTable { + public static final String TABLE_NAME = "notifications"; + public static final String ID = "_id"; + public static final String PACKAGE_ID = "package_id"; + public static final String MESSAGE_TEXT = "message_text"; + public static final String COUNT = "count"; + public static final String LAST_BLOCKED = "last_blocked"; + public static final String NORMALIZED_TEXT = "normalized_text"; + } + + } + + public static String getNormalizedContent(String msg) { + return msg.toLowerCase().replaceAll("[^\\p{L}\\p{Nd}]+", ""); + } + + public static String getNormalizedNotificationContent(Notification notification) { + String content = getNotificationContent(notification); + return getNormalizedContent(content); + } + + public static String getNotificationContent(Notification notification) { + CharSequence notificationTitle = getNotificationTitle(notification); + CharSequence notificationMessage = getNotificationMessage(notification); + return notificationTitle + "\n" + notificationMessage; + } + + private static CharSequence getNotificationTitle(Notification notification) { + Bundle extras = notification.extras; + String titleExtra = extras.containsKey(Notification.EXTRA_TITLE_BIG) + ? Notification.EXTRA_TITLE_BIG : Notification.EXTRA_TITLE; + CharSequence notificationTitle = extras.getCharSequence(titleExtra); + return notificationTitle; + } + + private static CharSequence getNotificationMessage(Notification notification) { + Bundle extras = notification.extras; + CharSequence notificationMessage = extras.getCharSequence(Notification.EXTRA_TEXT); + + if (TextUtils.isEmpty(notificationMessage)) { + CharSequence[] inboxLines = extras.getCharSequenceArray(Notification.EXTRA_TEXT_LINES); + if (inboxLines == null || inboxLines.length == 0) { + notificationMessage = ""; + } else { + notificationMessage = TextUtils.join("\n", inboxLines); + } + } + return notificationMessage; + } + + public static boolean hasFilterableContent(Notification notification) { + CharSequence notificationTitle = getNotificationTitle(notification); + CharSequence notificationMessage = getNotificationMessage(notification); + return !(TextUtils.isEmpty(notificationTitle) && TextUtils.isEmpty(notificationMessage)); + } +} diff --git a/core/java/com/android/internal/util/cm/palette/ColorCutQuantizer.java b/core/java/com/android/internal/util/cm/palette/ColorCutQuantizer.java new file mode 100644 index 0000000..a24dff8 --- /dev/null +++ b/core/java/com/android/internal/util/cm/palette/ColorCutQuantizer.java @@ -0,0 +1,516 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util.cm.palette; + +import android.graphics.Color; +import com.android.internal.util.cm.palette.Palette.Swatch; +import android.util.TimingLogger; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.PriorityQueue; + +/** + * An color quantizer based on the Median-cut algorithm, but optimized for picking out distinct + * colors rather than representation colors. + * + * The color space is represented as a 3-dimensional cube with each dimension being an RGB + * component. The cube is then repeatedly divided until we have reduced the color space to the + * requested number of colors. An average color is then generated from each cube. + * + * What makes this different to median-cut is that median-cut divided cubes so that all of the cubes + * have roughly the same population, where this quantizer divides boxes based on their color volume. + * This means that the color space is divided into distinct colors, rather than representative + * colors. + * + * @hide + */ +final class ColorCutQuantizer { + + private static final String LOG_TAG = "ColorCutQuantizer"; + private static final boolean LOG_TIMINGS = false; + + private static final int COMPONENT_RED = -3; + private static final int COMPONENT_GREEN = -2; + private static final int COMPONENT_BLUE = -1; + + private static final int QUANTIZE_WORD_WIDTH = 5; + private static final int QUANTIZE_WORD_MASK = (1 << QUANTIZE_WORD_WIDTH) - 1; + + final int[] mColors; + final int[] mHistogram; + final List<Swatch> mQuantizedColors; + final TimingLogger mTimingLogger; + final Palette.Filter[] mFilters; + + private final float[] mTempHsl = new float[3]; + + /** + * Constructor. + * + * @param pixels histogram representing an image's pixel data + * @param maxColors The maximum number of colors that should be in the result palette. + * @param filters Set of filters to use in the quantization stage + */ + ColorCutQuantizer(final int[] pixels, final int maxColors, final Palette.Filter[] filters) { + mTimingLogger = LOG_TIMINGS ? new TimingLogger(LOG_TAG, "Creation") : null; + mFilters = filters; + + final int[] hist = mHistogram = new int[1 << (QUANTIZE_WORD_WIDTH * 3)]; + for (int i = 0; i < pixels.length; i++) { + final int quantizedColor = quantizeFromRgb888(pixels[i]); + // Now update the pixel value to the quantized value + pixels[i] = quantizedColor; + // And update the histogram + hist[quantizedColor]++; + } + + if (LOG_TIMINGS) { + mTimingLogger.addSplit("Histogram created"); + } + + // Now let's count the number of distinct colors + int distinctColorCount = 0; + for (int color = 0; color < hist.length; color++) { + if (hist[color] > 0 && shouldIgnoreColor(color)) { + // If we should ignore the color, set the population to 0 + hist[color] = 0; + } + if (hist[color] > 0) { + // If the color has population, increase the distinct color count + distinctColorCount++; + } + } + + if (LOG_TIMINGS) { + mTimingLogger.addSplit("Filtered colors and distinct colors counted"); + } + + // Now lets go through create an array consisting of only distinct colors + final int[] colors = mColors = new int[distinctColorCount]; + int distinctColorIndex = 0; + for (int color = 0; color < hist.length; color++) { + if (hist[color] > 0) { + colors[distinctColorIndex++] = color; + } + } + + if (LOG_TIMINGS) { + mTimingLogger.addSplit("Distinct colors copied into array"); + } + + if (distinctColorCount <= maxColors) { + // The image has fewer colors than the maximum requested, so just return the colors + mQuantizedColors = new ArrayList<>(); + for (int color : colors) { + mQuantizedColors.add(new Swatch(approximateToRgb888(color), hist[color])); + } + + if (LOG_TIMINGS) { + mTimingLogger.addSplit("Too few colors present. Copied to Swatches"); + mTimingLogger.dumpToLog(); + } + } else { + // We need use quantization to reduce the number of colors + mQuantizedColors = quantizePixels(maxColors); + + if (LOG_TIMINGS) { + mTimingLogger.addSplit("Quantized colors computed"); + mTimingLogger.dumpToLog(); + } + } + } + + /** + * @return the list of quantized colors + */ + List<Swatch> getQuantizedColors() { + return mQuantizedColors; + } + + private List<Swatch> quantizePixels(int maxColors) { + // Create the priority queue which is sorted by volume descending. This means we always + // split the largest box in the queue + final PriorityQueue<Vbox> pq = new PriorityQueue<>(maxColors, VBOX_COMPARATOR_VOLUME); + + // To start, offer a box which contains all of the colors + pq.offer(new Vbox(0, mColors.length - 1)); + + // Now go through the boxes, splitting them until we have reached maxColors or there are no + // more boxes to split + splitBoxes(pq, maxColors); + + // Finally, return the average colors of the color boxes + return generateAverageColors(pq); + } + + /** + * Iterate through the {@link java.util.Queue}, popping + * {@link ColorCutQuantizer.Vbox} objects from the queue + * and splitting them. Once split, the new box and the remaining box are offered back to the + * queue. + * + * @param queue {@link java.util.PriorityQueue} to poll for boxes + * @param maxSize Maximum amount of boxes to split + */ + private void splitBoxes(final PriorityQueue<Vbox> queue, final int maxSize) { + while (queue.size() < maxSize) { + final Vbox vbox = queue.poll(); + + if (vbox != null && vbox.canSplit()) { + // First split the box, and offer the result + queue.offer(vbox.splitBox()); + + if (LOG_TIMINGS) { + mTimingLogger.addSplit("Box split"); + } + // Then offer the box back + queue.offer(vbox); + } else { + if (LOG_TIMINGS) { + mTimingLogger.addSplit("All boxes split"); + } + // If we get here then there are no more boxes to split, so return + return; + } + } + } + + private List<Swatch> generateAverageColors(Collection<Vbox> vboxes) { + ArrayList<Swatch> colors = new ArrayList<>(vboxes.size()); + for (Vbox vbox : vboxes) { + Swatch swatch = vbox.getAverageColor(); + if (!shouldIgnoreColor(swatch)) { + // As we're averaging a color box, we can still get colors which we do not want, so + // we check again here + colors.add(swatch); + } + } + return colors; + } + + /** + * Represents a tightly fitting box around a color space. + */ + private class Vbox { + // lower and upper index are inclusive + private int mLowerIndex; + private int mUpperIndex; + // Population of colors within this box + private int mPopulation; + + private int mMinRed, mMaxRed; + private int mMinGreen, mMaxGreen; + private int mMinBlue, mMaxBlue; + + Vbox(int lowerIndex, int upperIndex) { + mLowerIndex = lowerIndex; + mUpperIndex = upperIndex; + fitBox(); + } + + final int getVolume() { + return (mMaxRed - mMinRed + 1) * (mMaxGreen - mMinGreen + 1) * + (mMaxBlue - mMinBlue + 1); + } + + final boolean canSplit() { + return getColorCount() > 1; + } + + final int getColorCount() { + return 1 + mUpperIndex - mLowerIndex; + } + + /** + * Recomputes the boundaries of this box to tightly fit the colors within the box. + */ + final void fitBox() { + final int[] colors = mColors; + final int[] hist = mHistogram; + + // Reset the min and max to opposite values + int minRed, minGreen, minBlue; + minRed = minGreen = minBlue = Integer.MAX_VALUE; + int maxRed, maxGreen, maxBlue; + maxRed = maxGreen = maxBlue = Integer.MIN_VALUE; + int count = 0; + + for (int i = mLowerIndex; i <= mUpperIndex; i++) { + final int color = colors[i]; + count += hist[color]; + + final int r = quantizedRed(color); + final int g = quantizedGreen(color); + final int b = quantizedBlue(color); + if (r > maxRed) { + maxRed = r; + } + if (r < minRed) { + minRed = r; + } + if (g > maxGreen) { + maxGreen = g; + } + if (g < minGreen) { + minGreen = g; + } + if (b > maxBlue) { + maxBlue = b; + } + if (b < minBlue) { + minBlue = b; + } + } + + mMinRed = minRed; + mMaxRed = maxRed; + mMinGreen = minGreen; + mMaxGreen = maxGreen; + mMinBlue = minBlue; + mMaxBlue = maxBlue; + mPopulation = count; + } + + /** + * Split this color box at the mid-point along it's longest dimension + * + * @return the new ColorBox + */ + final Vbox splitBox() { + if (!canSplit()) { + throw new IllegalStateException("Can not split a box with only 1 color"); + } + + // find median along the longest dimension + final int splitPoint = findSplitPoint(); + + Vbox newBox = new Vbox(splitPoint + 1, mUpperIndex); + + // Now change this box's upperIndex and recompute the color boundaries + mUpperIndex = splitPoint; + fitBox(); + + return newBox; + } + + /** + * @return the dimension which this box is largest in + */ + final int getLongestColorDimension() { + final int redLength = mMaxRed - mMinRed; + final int greenLength = mMaxGreen - mMinGreen; + final int blueLength = mMaxBlue - mMinBlue; + + if (redLength >= greenLength && redLength >= blueLength) { + return COMPONENT_RED; + } else if (greenLength >= redLength && greenLength >= blueLength) { + return COMPONENT_GREEN; + } else { + return COMPONENT_BLUE; + } + } + + /** + * Finds the point within this box's lowerIndex and upperIndex index of where to split. + * + * This is calculated by finding the longest color dimension, and then sorting the + * sub-array based on that dimension value in each color. The colors are then iterated over + * until a color is found with at least the midpoint of the whole box's dimension midpoint. + * + * @return the index of the colors array to split from + */ + final int findSplitPoint() { + final int longestDimension = getLongestColorDimension(); + final int[] colors = mColors; + final int[] hist = mHistogram; + + // We need to sort the colors in this box based on the longest color dimension. + // As we can't use a Comparator to define the sort logic, we modify each color so that + // it's most significant is the desired dimension + modifySignificantOctet(colors, longestDimension, mLowerIndex, mUpperIndex); + + // Now sort... Arrays.sort uses a exclusive toIndex so we need to add 1 + Arrays.sort(colors, mLowerIndex, mUpperIndex + 1); + + // Now revert all of the colors so that they are packed as RGB again + modifySignificantOctet(colors, longestDimension, mLowerIndex, mUpperIndex); + + final int midPoint = mPopulation / 2; + for (int i = mLowerIndex, count = 0; i <= mUpperIndex; i++) { + count += hist[colors[i]]; + if (count >= midPoint) { + return i; + } + } + + return mLowerIndex; + } + + /** + * @return the average color of this box. + */ + final Swatch getAverageColor() { + final int[] colors = mColors; + final int[] hist = mHistogram; + int redSum = 0; + int greenSum = 0; + int blueSum = 0; + int totalPopulation = 0; + + for (int i = mLowerIndex; i <= mUpperIndex; i++) { + final int color = colors[i]; + final int colorPopulation = hist[color]; + + totalPopulation += colorPopulation; + redSum += colorPopulation * quantizedRed(color); + greenSum += colorPopulation * quantizedGreen(color); + blueSum += colorPopulation * quantizedBlue(color); + } + + final int redMean = Math.round(redSum / (float) totalPopulation); + final int greenMean = Math.round(greenSum / (float) totalPopulation); + final int blueMean = Math.round(blueSum / (float) totalPopulation); + + return new Swatch(approximateToRgb888(redMean, greenMean, blueMean), totalPopulation); + } + } + + /** + * Modify the significant octet in a packed color int. Allows sorting based on the value of a + * single color component. This relies on all components being the same word size. + * + * @see Vbox#findSplitPoint() + */ + private static void modifySignificantOctet(final int[] a, final int dimension, + final int lower, final int upper) { + switch (dimension) { + case COMPONENT_RED: + // Already in RGB, no need to do anything + break; + case COMPONENT_GREEN: + // We need to do a RGB to GRB swap, or vice-versa + for (int i = lower; i <= upper; i++) { + final int color = a[i]; + a[i] = quantizedGreen(color) << (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH) + | quantizedRed(color) << QUANTIZE_WORD_WIDTH + | quantizedBlue(color); + } + break; + case COMPONENT_BLUE: + // We need to do a RGB to BGR swap, or vice-versa + for (int i = lower; i <= upper; i++) { + final int color = a[i]; + a[i] = quantizedBlue(color) << (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH) + | quantizedGreen(color) << QUANTIZE_WORD_WIDTH + | quantizedRed(color); + } + break; + } + } + + private boolean shouldIgnoreColor(int color565) { + final int rgb = approximateToRgb888(color565); + ColorUtils.colorToHSL(rgb, mTempHsl); + return shouldIgnoreColor(rgb, mTempHsl); + } + + private boolean shouldIgnoreColor(Swatch color) { + return shouldIgnoreColor(color.getRgb(), color.getHsl()); + } + + private boolean shouldIgnoreColor(int rgb, float[] hsl) { + if (mFilters != null && mFilters.length > 0) { + for (int i = 0, count = mFilters.length; i < count; i++) { + if (!mFilters[i].isAllowed(rgb, hsl)) { + return true; + } + } + } + return false; + } + + /** + * Comparator which sorts {@link Vbox} instances based on their volume, in descending order + */ + private static final Comparator<Vbox> VBOX_COMPARATOR_VOLUME = new Comparator<Vbox>() { + @Override + public int compare(Vbox lhs, Vbox rhs) { + return rhs.getVolume() - lhs.getVolume(); + } + }; + + /** + * Quantized a RGB888 value to have a word width of {@value #QUANTIZE_WORD_WIDTH}. + */ + private static int quantizeFromRgb888(int color) { + int r = modifyWordWidth(Color.red(color), 8, QUANTIZE_WORD_WIDTH); + int g = modifyWordWidth(Color.green(color), 8, QUANTIZE_WORD_WIDTH); + int b = modifyWordWidth(Color.blue(color), 8, QUANTIZE_WORD_WIDTH); + return r << (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH) | g << QUANTIZE_WORD_WIDTH | b; + } + + /** + * Quantized RGB888 values to have a word width of {@value #QUANTIZE_WORD_WIDTH}. + */ + private static int approximateToRgb888(int r, int g, int b) { + return Color.rgb(modifyWordWidth(r, QUANTIZE_WORD_WIDTH, 8), + modifyWordWidth(g, QUANTIZE_WORD_WIDTH, 8), + modifyWordWidth(b, QUANTIZE_WORD_WIDTH, 8)); + } + + private static int approximateToRgb888(int color) { + return approximateToRgb888(quantizedRed(color), quantizedGreen(color), quantizedBlue(color)); + } + + /** + * @return red component of the quantized color + */ + private static int quantizedRed(int color) { + return (color >> (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH)) & QUANTIZE_WORD_MASK; + } + + /** + * @return green component of a quantized color + */ + private static int quantizedGreen(int color) { + return (color >> QUANTIZE_WORD_WIDTH) & QUANTIZE_WORD_MASK; + } + + /** + * @return blue component of a quantized color + */ + private static int quantizedBlue(int color) { + return color & QUANTIZE_WORD_MASK; + } + + private static int modifyWordWidth(int value, int currentWidth, int targetWidth) { + final int newValue; + if (targetWidth > currentWidth) { + // If we're approximating up in word width, we'll shift up + newValue = value << (targetWidth - currentWidth); + } else { + // Else, we will just shift and keep the MSB + newValue = value >> (currentWidth - targetWidth); + } + return newValue & ((1 << targetWidth) - 1); + } + +} diff --git a/core/java/com/android/internal/util/cm/palette/ColorUtils.java b/core/java/com/android/internal/util/cm/palette/ColorUtils.java new file mode 100644 index 0000000..8dcf750 --- /dev/null +++ b/core/java/com/android/internal/util/cm/palette/ColorUtils.java @@ -0,0 +1,299 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util.cm.palette; + +import android.graphics.Color; + +/** + * A set of color-related utility methods, building upon those available in {@code Color}. + * + * @hide + */ +public class ColorUtils { + + private static final int MIN_ALPHA_SEARCH_MAX_ITERATIONS = 10; + private static final int MIN_ALPHA_SEARCH_PRECISION = 10; + + private ColorUtils() {} + + /** + * Composite two potentially translucent colors over each other and returns the result. + */ + public static int compositeColors(int foreground, int background) { + int bgAlpha = Color.alpha(background); + int fgAlpha = Color.alpha(foreground); + int a = compositeAlpha(fgAlpha, bgAlpha); + + int r = compositeComponent(Color.red(foreground), fgAlpha, + Color.red(background), bgAlpha, a); + int g = compositeComponent(Color.green(foreground), fgAlpha, + Color.green(background), bgAlpha, a); + int b = compositeComponent(Color.blue(foreground), fgAlpha, + Color.blue(background), bgAlpha, a); + + return Color.argb(a, r, g, b); + } + + private static int compositeAlpha(int foregroundAlpha, int backgroundAlpha) { + return 0xFF - (((0xFF - backgroundAlpha) * (0xFF - foregroundAlpha)) / 0xFF); + } + + private static int compositeComponent(int fgC, int fgA, int bgC, int bgA, int a) { + if (a == 0) return 0; + return ((0xFF * fgC * fgA) + (bgC * bgA * (0xFF - fgA))) / (a * 0xFF); + } + + /** + * Returns the luminance of a color. + * + * Formula defined here: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef + */ + public static double calculateLuminance(int color) { + double red = Color.red(color) / 255d; + red = red < 0.03928 ? red / 12.92 : Math.pow((red + 0.055) / 1.055, 2.4); + + double green = Color.green(color) / 255d; + green = green < 0.03928 ? green / 12.92 : Math.pow((green + 0.055) / 1.055, 2.4); + + double blue = Color.blue(color) / 255d; + blue = blue < 0.03928 ? blue / 12.92 : Math.pow((blue + 0.055) / 1.055, 2.4); + + return (0.2126 * red) + (0.7152 * green) + (0.0722 * blue); + } + + /** + * Returns the contrast ratio between {@code foreground} and {@code background}. + * {@code background} must be opaque. + * <p> + * Formula defined + * <a href="http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef">here</a>. + */ + public static double calculateContrast(int foreground, int background) { + if (Color.alpha(background) != 255) { + throw new IllegalArgumentException("background can not be translucent"); + } + if (Color.alpha(foreground) < 255) { + // If the foreground is translucent, composite the foreground over the background + foreground = compositeColors(foreground, background); + } + + final double luminance1 = calculateLuminance(foreground) + 0.05; + final double luminance2 = calculateLuminance(background) + 0.05; + + // Now return the lighter luminance divided by the darker luminance + return Math.max(luminance1, luminance2) / Math.min(luminance1, luminance2); + } + + /** + * Calculates the minimum alpha value which can be applied to {@code foreground} so that would + * have a contrast value of at least {@code minContrastRatio} when compared to + * {@code background}. + * + * @param foreground the foreground color. + * @param background the background color. Should be opaque. + * @param minContrastRatio the minimum contrast ratio. + * @return the alpha value in the range 0-255, or -1 if no value could be calculated. + */ + public static int calculateMinimumAlpha(int foreground, int background, + float minContrastRatio) { + if (Color.alpha(background) != 255) { + throw new IllegalArgumentException("background can not be translucent"); + } + + // First lets check that a fully opaque foreground has sufficient contrast + int testForeground = setAlphaComponent(foreground, 255); + double testRatio = calculateContrast(testForeground, background); + if (testRatio < minContrastRatio) { + // Fully opaque foreground does not have sufficient contrast, return error + return -1; + } + + // Binary search to find a value with the minimum value which provides sufficient contrast + int numIterations = 0; + int minAlpha = 0; + int maxAlpha = 255; + + while (numIterations <= MIN_ALPHA_SEARCH_MAX_ITERATIONS && + (maxAlpha - minAlpha) > MIN_ALPHA_SEARCH_PRECISION) { + final int testAlpha = (minAlpha + maxAlpha) / 2; + + testForeground = setAlphaComponent(foreground, testAlpha); + testRatio = calculateContrast(testForeground, background); + + if (testRatio < minContrastRatio) { + minAlpha = testAlpha; + } else { + maxAlpha = testAlpha; + } + + numIterations++; + } + + // Conservatively return the max of the range of possible alphas, which is known to pass. + return maxAlpha; + } + + /** + * Convert RGB components to HSL (hue-saturation-lightness). + * <ul> + * <li>hsl[0] is Hue [0 .. 360)</li> + * <li>hsl[1] is Saturation [0...1]</li> + * <li>hsl[2] is Lightness [0...1]</li> + * </ul> + * + * @param r red component value [0..255] + * @param g green component value [0..255] + * @param b blue component value [0..255] + * @param hsl 3 element array which holds the resulting HSL components. + */ + public static void RGBToHSL(int r, int g, int b, float[] hsl) { + final float rf = r / 255f; + final float gf = g / 255f; + final float bf = b / 255f; + + final float max = Math.max(rf, Math.max(gf, bf)); + final float min = Math.min(rf, Math.min(gf, bf)); + final float deltaMaxMin = max - min; + + float h, s; + float l = (max + min) / 2f; + + if (max == min) { + // Monochromatic + h = s = 0f; + } else { + if (max == rf) { + h = ((gf - bf) / deltaMaxMin) % 6f; + } else if (max == gf) { + h = ((bf - rf) / deltaMaxMin) + 2f; + } else { + h = ((rf - gf) / deltaMaxMin) + 4f; + } + + s = deltaMaxMin / (1f - Math.abs(2f * l - 1f)); + } + + h = (h * 60f) % 360f; + if (h < 0) { + h += 360f; + } + + hsl[0] = constrain(h, 0f, 360f); + hsl[1] = constrain(s, 0f, 1f); + hsl[2] = constrain(l, 0f, 1f); + } + + /** + * Convert the ARGB color to its HSL (hue-saturation-lightness) components. + * <ul> + * <li>hsl[0] is Hue [0 .. 360)</li> + * <li>hsl[1] is Saturation [0...1]</li> + * <li>hsl[2] is Lightness [0...1]</li> + * </ul> + * + * @param color the ARGB color to convert. The alpha component is ignored. + * @param hsl 3 element array which holds the resulting HSL components. + */ + public static void colorToHSL(int color, float[] hsl) { + RGBToHSL(Color.red(color), Color.green(color), Color.blue(color), hsl); + } + + /** + * Convert HSL (hue-saturation-lightness) components to a RGB color. + * <ul> + * <li>hsl[0] is Hue [0 .. 360)</li> + * <li>hsl[1] is Saturation [0...1]</li> + * <li>hsl[2] is Lightness [0...1]</li> + * </ul> + * If hsv values are out of range, they are pinned. + * + * @param hsl 3 element array which holds the input HSL components. + * @return the resulting RGB color + */ + public static int HSLToColor(float[] hsl) { + final float h = hsl[0]; + final float s = hsl[1]; + final float l = hsl[2]; + + final float c = (1f - Math.abs(2 * l - 1f)) * s; + final float m = l - 0.5f * c; + final float x = c * (1f - Math.abs((h / 60f % 2f) - 1f)); + + final int hueSegment = (int) h / 60; + + int r = 0, g = 0, b = 0; + + switch (hueSegment) { + case 0: + r = Math.round(255 * (c + m)); + g = Math.round(255 * (x + m)); + b = Math.round(255 * m); + break; + case 1: + r = Math.round(255 * (x + m)); + g = Math.round(255 * (c + m)); + b = Math.round(255 * m); + break; + case 2: + r = Math.round(255 * m); + g = Math.round(255 * (c + m)); + b = Math.round(255 * (x + m)); + break; + case 3: + r = Math.round(255 * m); + g = Math.round(255 * (x + m)); + b = Math.round(255 * (c + m)); + break; + case 4: + r = Math.round(255 * (x + m)); + g = Math.round(255 * m); + b = Math.round(255 * (c + m)); + break; + case 5: + case 6: + r = Math.round(255 * (c + m)); + g = Math.round(255 * m); + b = Math.round(255 * (x + m)); + break; + } + + r = constrain(r, 0, 255); + g = constrain(g, 0, 255); + b = constrain(b, 0, 255); + + return Color.rgb(r, g, b); + } + + /** + * Set the alpha component of {@code color} to be {@code alpha}. + */ + public static int setAlphaComponent(int color, int alpha) { + if (alpha < 0 || alpha > 255) { + throw new IllegalArgumentException("alpha must be between 0 and 255."); + } + return (color & 0x00ffffff) | (alpha << 24); + } + + private static float constrain(float amount, float low, float high) { + return amount < low ? low : (amount > high ? high : amount); + } + + private static int constrain(int amount, int low, int high) { + return amount < low ? low : (amount > high ? high : amount); + } + +} diff --git a/core/java/com/android/internal/util/cm/palette/DefaultGenerator.java b/core/java/com/android/internal/util/cm/palette/DefaultGenerator.java new file mode 100644 index 0000000..407f01d --- /dev/null +++ b/core/java/com/android/internal/util/cm/palette/DefaultGenerator.java @@ -0,0 +1,244 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util.cm.palette; + +import com.android.internal.util.cm.palette.Palette.Swatch; + +import java.util.List; + +/** + * @hide + */ +class DefaultGenerator extends Palette.Generator { + + private static final float TARGET_DARK_LUMA = 0.26f; + private static final float MAX_DARK_LUMA = 0.45f; + + private static final float MIN_LIGHT_LUMA = 0.55f; + private static final float TARGET_LIGHT_LUMA = 0.74f; + + private static final float MIN_NORMAL_LUMA = 0.3f; + private static final float TARGET_NORMAL_LUMA = 0.5f; + private static final float MAX_NORMAL_LUMA = 0.7f; + + private static final float TARGET_MUTED_SATURATION = 0.3f; + private static final float MAX_MUTED_SATURATION = 0.4f; + + private static final float TARGET_VIBRANT_SATURATION = 1f; + private static final float MIN_VIBRANT_SATURATION = 0.35f; + + private static final float WEIGHT_SATURATION = 3f; + private static final float WEIGHT_LUMA = 6f; + private static final float WEIGHT_POPULATION = 1f; + + private List<Swatch> mSwatches; + + private int mHighestPopulation; + + private Swatch mVibrantSwatch; + private Swatch mMutedSwatch; + private Swatch mDarkVibrantSwatch; + private Swatch mDarkMutedSwatch; + private Swatch mLightVibrantSwatch; + private Swatch mLightMutedSwatch; + + @Override + public void generate(final List<Swatch> swatches) { + mSwatches = swatches; + + mHighestPopulation = findMaxPopulation(); + + generateVariationColors(); + + // Now try and generate any missing colors + generateEmptySwatches(); + } + + @Override + public Swatch getVibrantSwatch() { + return mVibrantSwatch; + } + + @Override + public Swatch getLightVibrantSwatch() { + return mLightVibrantSwatch; + } + + @Override + public Swatch getDarkVibrantSwatch() { + return mDarkVibrantSwatch; + } + + @Override + public Swatch getMutedSwatch() { + return mMutedSwatch; + } + + @Override + public Swatch getLightMutedSwatch() { + return mLightMutedSwatch; + } + + @Override + public Swatch getDarkMutedSwatch() { + return mDarkMutedSwatch; + } + + private void generateVariationColors() { + mVibrantSwatch = findColorVariation(TARGET_NORMAL_LUMA, MIN_NORMAL_LUMA, MAX_NORMAL_LUMA, + TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f); + + mLightVibrantSwatch = findColorVariation(TARGET_LIGHT_LUMA, MIN_LIGHT_LUMA, 1f, + TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f); + + mDarkVibrantSwatch = findColorVariation(TARGET_DARK_LUMA, 0f, MAX_DARK_LUMA, + TARGET_VIBRANT_SATURATION, MIN_VIBRANT_SATURATION, 1f); + + mMutedSwatch = findColorVariation(TARGET_NORMAL_LUMA, MIN_NORMAL_LUMA, MAX_NORMAL_LUMA, + TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION); + + mLightMutedSwatch = findColorVariation(TARGET_LIGHT_LUMA, MIN_LIGHT_LUMA, 1f, + TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION); + + mDarkMutedSwatch = findColorVariation(TARGET_DARK_LUMA, 0f, MAX_DARK_LUMA, + TARGET_MUTED_SATURATION, 0f, MAX_MUTED_SATURATION); + } + + /** + * Try and generate any missing swatches from the swatches we did find. + */ + private void generateEmptySwatches() { + if (mVibrantSwatch == null) { + // If we do not have a vibrant color... + if (mDarkVibrantSwatch != null) { + // ...but we do have a dark vibrant, generate the value by modifying the luma + final float[] newHsl = copyHslValues(mDarkVibrantSwatch); + newHsl[2] = TARGET_NORMAL_LUMA; + mVibrantSwatch = new Swatch(ColorUtils.HSLToColor(newHsl), 0); + } + } + + if (mDarkVibrantSwatch == null) { + // If we do not have a dark vibrant color... + if (mVibrantSwatch != null) { + // ...but we do have a vibrant, generate the value by modifying the luma + final float[] newHsl = copyHslValues(mVibrantSwatch); + newHsl[2] = TARGET_DARK_LUMA; + mDarkVibrantSwatch = new Swatch(ColorUtils.HSLToColor(newHsl), 0); + } + } + } + + /** + * Find the {@link Palette.Swatch} with the highest population value and return the population. + */ + private int findMaxPopulation() { + int population = 0; + for (Swatch swatch : mSwatches) { + population = Math.max(population, swatch.getPopulation()); + } + return population; + } + + private Swatch findColorVariation(float targetLuma, float minLuma, float maxLuma, + float targetSaturation, float minSaturation, float maxSaturation) { + Swatch max = null; + float maxValue = 0f; + + for (Swatch swatch : mSwatches) { + final float sat = swatch.getHsl()[1]; + final float luma = swatch.getHsl()[2]; + + if (sat >= minSaturation && sat <= maxSaturation && + luma >= minLuma && luma <= maxLuma && + !isAlreadySelected(swatch)) { + float value = createComparisonValue(sat, targetSaturation, luma, targetLuma, + swatch.getPopulation(), mHighestPopulation); + if (max == null || value > maxValue) { + max = swatch; + maxValue = value; + } + } + } + + return max; + } + + /** + * @return true if we have already selected {@code swatch} + */ + private boolean isAlreadySelected(Swatch swatch) { + return mVibrantSwatch == swatch || mDarkVibrantSwatch == swatch || + mLightVibrantSwatch == swatch || mMutedSwatch == swatch || + mDarkMutedSwatch == swatch || mLightMutedSwatch == swatch; + } + + private static float createComparisonValue(float saturation, float targetSaturation, + float luma, float targetLuma, + int population, int maxPopulation) { + return createComparisonValue(saturation, targetSaturation, WEIGHT_SATURATION, + luma, targetLuma, WEIGHT_LUMA, + population, maxPopulation, WEIGHT_POPULATION); + } + + private static float createComparisonValue( + float saturation, float targetSaturation, float saturationWeight, + float luma, float targetLuma, float lumaWeight, + int population, int maxPopulation, float populationWeight) { + return weightedMean( + invertDiff(saturation, targetSaturation), saturationWeight, + invertDiff(luma, targetLuma), lumaWeight, + population / (float) maxPopulation, populationWeight + ); + } + + /** + * Copy a {@link Swatch}'s HSL values into a new float[]. + */ + private static float[] copyHslValues(Swatch color) { + final float[] newHsl = new float[3]; + System.arraycopy(color.getHsl(), 0, newHsl, 0, 3); + return newHsl; + } + + /** + * Returns a value in the range 0-1. 1 is returned when {@code value} equals the + * {@code targetValue} and then decreases as the absolute difference between {@code value} and + * {@code targetValue} increases. + * + * @param value the item's value + * @param targetValue the value which we desire + */ + private static float invertDiff(float value, float targetValue) { + return 1f - Math.abs(value - targetValue); + } + + private static float weightedMean(float... values) { + float sum = 0f; + float sumWeight = 0f; + + for (int i = 0; i < values.length; i += 2) { + float value = values[i]; + float weight = values[i + 1]; + + sum += (value * weight); + sumWeight += weight; + } + + return sum / sumWeight; + } +} diff --git a/core/java/com/android/internal/util/cm/palette/Palette.java b/core/java/com/android/internal/util/cm/palette/Palette.java new file mode 100644 index 0000000..2cbd2b8 --- /dev/null +++ b/core/java/com/android/internal/util/cm/palette/Palette.java @@ -0,0 +1,740 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util.cm.palette; + +import android.graphics.Bitmap; +import android.graphics.Color; +import android.os.AsyncTask; +import android.annotation.ColorInt; +import android.annotation.Nullable; +import android.util.TimingLogger; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * A helper class to extract prominent colors from an image. + * <p> + * A number of colors with different profiles are extracted from the image: + * <ul> + * <li>Vibrant</li> + * <li>Vibrant Dark</li> + * <li>Vibrant Light</li> + * <li>Muted</li> + * <li>Muted Dark</li> + * <li>Muted Light</li> + * </ul> + * These can be retrieved from the appropriate getter method. + * + * <p> + * Instances are created with a {@link Builder} which supports several options to tweak the + * generated Palette. See that class' documentation for more information. + * <p> + * Generation should always be completed on a background thread, ideally the one in + * which you load your image on. {@link Builder} supports both synchronous and asynchronous + * generation: + * + * <pre> + * // Synchronous + * Palette p = Palette.from(bitmap).generate(); + * + * // Asynchronous + * Palette.from(bitmap).generate(new PaletteAsyncListener() { + * public void onGenerated(Palette p) { + * // Use generated instance + * } + * }); + * </pre> + * + * @hide + */ +public final class Palette { + + /** + * Listener to be used with {@link #generateAsync(Bitmap, PaletteAsyncListener)} or + * {@link #generateAsync(Bitmap, int, PaletteAsyncListener)} + */ + public interface PaletteAsyncListener { + + /** + * Called when the {@link Palette} has been generated. + */ + void onGenerated(Palette palette); + } + + private static final int DEFAULT_RESIZE_BITMAP_MAX_DIMENSION = 192; + private static final int DEFAULT_CALCULATE_NUMBER_COLORS = 16; + + private static final float MIN_CONTRAST_TITLE_TEXT = 3.0f; + private static final float MIN_CONTRAST_BODY_TEXT = 4.5f; + + private static final String LOG_TAG = "Palette"; + private static final boolean LOG_TIMINGS = false; + + /** + * Start generating a {@link Palette} with the returned {@link Builder} instance. + */ + public static Builder from(Bitmap bitmap) { + return new Builder(bitmap); + } + + /** + * Generate a {@link Palette} from the pre-generated list of {@link Palette.Swatch} swatches. + * This is useful for testing, or if you want to resurrect a {@link Palette} instance from a + * list of swatches. Will return null if the {@code swatches} is null. + */ + public static Palette from(List<Swatch> swatches) { + return new Builder(swatches).generate(); + } + + /** + * @deprecated Use {@link Builder} to generate the Palette. + */ + @Deprecated + public static Palette generate(Bitmap bitmap) { + return from(bitmap).generate(); + } + + /** + * @deprecated Use {@link Builder} to generate the Palette. + */ + @Deprecated + public static Palette generate(Bitmap bitmap, int numColors) { + return from(bitmap).maximumColorCount(numColors).generate(); + } + + /** + * @deprecated Use {@link Builder} to generate the Palette. + */ + @Deprecated + public static AsyncTask<Bitmap, Void, Palette> generateAsync( + Bitmap bitmap, PaletteAsyncListener listener) { + return from(bitmap).generate(listener); + } + + /** + * @deprecated Use {@link Builder} to generate the Palette. + */ + @Deprecated + public static AsyncTask<Bitmap, Void, Palette> generateAsync( + final Bitmap bitmap, final int numColors, final PaletteAsyncListener listener) { + return from(bitmap).maximumColorCount(numColors).generate(listener); + } + + private final List<Swatch> mSwatches; + private final Generator mGenerator; + + private Palette(List<Swatch> swatches, Generator generator) { + mSwatches = swatches; + mGenerator = generator; + } + + /** + * Returns all of the swatches which make up the palette. + */ + public List<Swatch> getSwatches() { + return Collections.unmodifiableList(mSwatches); + } + + /** + * Returns the most vibrant swatch in the palette. Might be null. + */ + @Nullable + public Swatch getVibrantSwatch() { + return mGenerator.getVibrantSwatch(); + } + + /** + * Returns a light and vibrant swatch from the palette. Might be null. + */ + @Nullable + public Swatch getLightVibrantSwatch() { + return mGenerator.getLightVibrantSwatch(); + } + + /** + * Returns a dark and vibrant swatch from the palette. Might be null. + */ + @Nullable + public Swatch getDarkVibrantSwatch() { + return mGenerator.getDarkVibrantSwatch(); + } + + /** + * Returns a muted swatch from the palette. Might be null. + */ + @Nullable + public Swatch getMutedSwatch() { + return mGenerator.getMutedSwatch(); + } + + /** + * Returns a muted and light swatch from the palette. Might be null. + */ + @Nullable + public Swatch getLightMutedSwatch() { + return mGenerator.getLightMutedSwatch(); + } + + /** + * Returns a muted and dark swatch from the palette. Might be null. + */ + @Nullable + public Swatch getDarkMutedSwatch() { + return mGenerator.getDarkMutedSwatch(); + } + + /** + * Returns the most vibrant color in the palette as an RGB packed int. + * + * @param defaultColor value to return if the swatch isn't available + */ + @ColorInt + public int getVibrantColor(@ColorInt int defaultColor) { + Swatch swatch = getVibrantSwatch(); + return swatch != null ? swatch.getRgb() : defaultColor; + } + + /** + * Returns a light and vibrant color from the palette as an RGB packed int. + * + * @param defaultColor value to return if the swatch isn't available + */ + @ColorInt + public int getLightVibrantColor(@ColorInt int defaultColor) { + Swatch swatch = getLightVibrantSwatch(); + return swatch != null ? swatch.getRgb() : defaultColor; + } + + /** + * Returns a dark and vibrant color from the palette as an RGB packed int. + * + * @param defaultColor value to return if the swatch isn't available + */ + @ColorInt + public int getDarkVibrantColor(@ColorInt int defaultColor) { + Swatch swatch = getDarkVibrantSwatch(); + return swatch != null ? swatch.getRgb() : defaultColor; + } + + /** + * Returns a muted color from the palette as an RGB packed int. + * + * @param defaultColor value to return if the swatch isn't available + */ + @ColorInt + public int getMutedColor(@ColorInt int defaultColor) { + Swatch swatch = getMutedSwatch(); + return swatch != null ? swatch.getRgb() : defaultColor; + } + + /** + * Returns a muted and light color from the palette as an RGB packed int. + * + * @param defaultColor value to return if the swatch isn't available + */ + @ColorInt + public int getLightMutedColor(@ColorInt int defaultColor) { + Swatch swatch = getLightMutedSwatch(); + return swatch != null ? swatch.getRgb() : defaultColor; + } + + /** + * Returns a muted and dark color from the palette as an RGB packed int. + * + * @param defaultColor value to return if the swatch isn't available + */ + @ColorInt + public int getDarkMutedColor(@ColorInt int defaultColor) { + Swatch swatch = getDarkMutedSwatch(); + return swatch != null ? swatch.getRgb() : defaultColor; + } + + /** + * Scale the bitmap down so that it's largest dimension is {@code targetMaxDimension}. + * If {@code bitmap} is smaller than this, then it is returned. + */ + private static Bitmap scaleBitmapDown(Bitmap bitmap, final int targetMaxDimension) { + final int maxDimension = Math.max(bitmap.getWidth(), bitmap.getHeight()); + + if (maxDimension <= targetMaxDimension) { + // If the bitmap is small enough already, just return it + return bitmap; + } + + final float scaleRatio = targetMaxDimension / (float) maxDimension; + return Bitmap.createScaledBitmap(bitmap, + Math.round(bitmap.getWidth() * scaleRatio), + Math.round(bitmap.getHeight() * scaleRatio), + false); + } + + /** + * Represents a color swatch generated from an image's palette. The RGB color can be retrieved + * by calling {@link #getRgb()}. + */ + public static final class Swatch { + private final int mRed, mGreen, mBlue; + private final int mRgb; + private final int mPopulation; + + private boolean mGeneratedTextColors; + private int mTitleTextColor; + private int mBodyTextColor; + + private float[] mHsl; + + public Swatch(@ColorInt int color, int population) { + mRed = Color.red(color); + mGreen = Color.green(color); + mBlue = Color.blue(color); + mRgb = color; + mPopulation = population; + } + + Swatch(int red, int green, int blue, int population) { + mRed = red; + mGreen = green; + mBlue = blue; + mRgb = Color.rgb(red, green, blue); + mPopulation = population; + } + + /** + * @return this swatch's RGB color value + */ + @ColorInt + public int getRgb() { + return mRgb; + } + + /** + * Return this swatch's HSL values. + * hsv[0] is Hue [0 .. 360) + * hsv[1] is Saturation [0...1] + * hsv[2] is Lightness [0...1] + */ + public float[] getHsl() { + if (mHsl == null) { + mHsl = new float[3]; + ColorUtils.RGBToHSL(mRed, mGreen, mBlue, mHsl); + } + return mHsl; + } + + /** + * @return the number of pixels represented by this swatch + */ + public int getPopulation() { + return mPopulation; + } + + /** + * Returns an appropriate color to use for any 'title' text which is displayed over this + * {@link Swatch}'s color. This color is guaranteed to have sufficient contrast. + */ + @ColorInt + public int getTitleTextColor() { + ensureTextColorsGenerated(); + return mTitleTextColor; + } + + /** + * Returns an appropriate color to use for any 'body' text which is displayed over this + * {@link Swatch}'s color. This color is guaranteed to have sufficient contrast. + */ + @ColorInt + public int getBodyTextColor() { + ensureTextColorsGenerated(); + return mBodyTextColor; + } + + private void ensureTextColorsGenerated() { + if (!mGeneratedTextColors) { + // First check white, as most colors will be dark + final int lightBodyAlpha = ColorUtils.calculateMinimumAlpha( + Color.WHITE, mRgb, MIN_CONTRAST_BODY_TEXT); + final int lightTitleAlpha = ColorUtils.calculateMinimumAlpha( + Color.WHITE, mRgb, MIN_CONTRAST_TITLE_TEXT); + + if (lightBodyAlpha != -1 && lightTitleAlpha != -1) { + // If we found valid light values, use them and return + mBodyTextColor = ColorUtils.setAlphaComponent(Color.WHITE, lightBodyAlpha); + mTitleTextColor = ColorUtils.setAlphaComponent(Color.WHITE, lightTitleAlpha); + mGeneratedTextColors = true; + return; + } + + final int darkBodyAlpha = ColorUtils.calculateMinimumAlpha( + Color.BLACK, mRgb, MIN_CONTRAST_BODY_TEXT); + final int darkTitleAlpha = ColorUtils.calculateMinimumAlpha( + Color.BLACK, mRgb, MIN_CONTRAST_TITLE_TEXT); + + if (darkBodyAlpha != -1 && darkBodyAlpha != -1) { + // If we found valid dark values, use them and return + mBodyTextColor = ColorUtils.setAlphaComponent(Color.BLACK, darkBodyAlpha); + mTitleTextColor = ColorUtils.setAlphaComponent(Color.BLACK, darkTitleAlpha); + mGeneratedTextColors = true; + return; + } + + // If we reach here then we can not find title and body values which use the same + // lightness, we need to use mismatched values + mBodyTextColor = lightBodyAlpha != -1 + ? ColorUtils.setAlphaComponent(Color.WHITE, lightBodyAlpha) + : ColorUtils.setAlphaComponent(Color.BLACK, darkBodyAlpha); + mTitleTextColor = lightTitleAlpha != -1 + ? ColorUtils.setAlphaComponent(Color.WHITE, lightTitleAlpha) + : ColorUtils.setAlphaComponent(Color.BLACK, darkTitleAlpha); + mGeneratedTextColors = true; + } + } + + @Override + public String toString() { + return new StringBuilder(getClass().getSimpleName()) + .append(" [RGB: #").append(Integer.toHexString(getRgb())).append(']') + .append(" [HSL: ").append(Arrays.toString(getHsl())).append(']') + .append(" [Population: ").append(mPopulation).append(']') + .append(" [Title Text: #").append(Integer.toHexString(getTitleTextColor())) + .append(']') + .append(" [Body Text: #").append(Integer.toHexString(getBodyTextColor())) + .append(']').toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Swatch swatch = (Swatch) o; + return mPopulation == swatch.mPopulation && mRgb == swatch.mRgb; + } + + @Override + public int hashCode() { + return 31 * mRgb + mPopulation; + } + } + + /** + * Builder class for generating {@link Palette} instances. + */ + public static final class Builder { + private List<Swatch> mSwatches; + private Bitmap mBitmap; + private int mMaxColors = DEFAULT_CALCULATE_NUMBER_COLORS; + private int mResizeMaxDimension = DEFAULT_RESIZE_BITMAP_MAX_DIMENSION; + private final List<Filter> mFilters = new ArrayList<>(); + + private Generator mGenerator; + + /** + * Construct a new {@link Builder} using a source {@link Bitmap} + */ + public Builder(Bitmap bitmap) { + this(); + if (bitmap == null || bitmap.isRecycled()) { + throw new IllegalArgumentException("Bitmap is not valid"); + } + mBitmap = bitmap; + } + + /** + * Construct a new {@link Builder} using a list of {@link Swatch} instances. + * Typically only used for testing. + */ + public Builder(List<Swatch> swatches) { + this(); + if (swatches == null || swatches.isEmpty()) { + throw new IllegalArgumentException("List of Swatches is not valid"); + } + mSwatches = swatches; + } + + private Builder() { + mFilters.add(DEFAULT_FILTER); + } + + /** + * Set the {@link Generator} to use when generating the {@link Palette}. If this is called + * with {@code null} then the default generator will be used. + */ + Builder generator(Generator generator) { + mGenerator = generator; + return this; + } + + /** + * Set the maximum number of colors to use in the quantization step when using a + * {@link android.graphics.Bitmap} as the source. + * <p> + * Good values for depend on the source image type. For landscapes, good values are in + * the range 10-16. For images which are largely made up of people's faces then this + * value should be increased to ~24. + */ + public Builder maximumColorCount(int colors) { + mMaxColors = colors; + return this; + } + + /** + * Set the resize value when using a {@link android.graphics.Bitmap} as the source. + * If the bitmap's largest dimension is greater than the value specified, then the bitmap + * will be resized so that it's largest dimension matches {@code maxDimension}. If the + * bitmap is smaller or equal, the original is used as-is. + * <p> + * This value has a large effect on the processing time. The larger the resized image is, + * the greater time it will take to generate the palette. The smaller the image is, the + * more detail is lost in the resulting image and thus less precision for color selection. + */ + public Builder resizeBitmapSize(int maxDimension) { + mResizeMaxDimension = maxDimension; + return this; + } + + /** + * Clear all added filters. This includes any default filters added automatically by + * {@link Palette}. + */ + public Builder clearFilters() { + mFilters.clear(); + return this; + } + + /** + * Add a filter to be able to have fine grained controlled over the colors which are + * allowed in the resulting palette. + * + * @param filter filter to add. + */ + public Builder addFilter(Filter filter) { + if (filter != null) { + mFilters.add(filter); + } + return this; + } + + /** + * Generate and return the {@link Palette} synchronously. + */ + public Palette generate() { + final TimingLogger logger = LOG_TIMINGS + ? new TimingLogger(LOG_TAG, "Generation") + : null; + + List<Swatch> swatches; + + if (mBitmap != null) { + // We have a Bitmap so we need to quantization to reduce the number of colors + + if (mResizeMaxDimension <= 0) { + throw new IllegalArgumentException( + "Minimum dimension size for resizing should should be >= 1"); + } + + // First we'll scale down the bitmap so it's largest dimension is as specified + final Bitmap scaledBitmap = scaleBitmapDown(mBitmap, mResizeMaxDimension); + + if (logger != null) { + logger.addSplit("Processed Bitmap"); + } + + // Now generate a quantizer from the Bitmap + final int width = scaledBitmap.getWidth(); + final int height = scaledBitmap.getHeight(); + final int[] pixels = new int[width * height]; + scaledBitmap.getPixels(pixels, 0, width, 0, 0, width, height); + + final ColorCutQuantizer quantizer = new ColorCutQuantizer(pixels, mMaxColors, + mFilters.isEmpty() ? null : mFilters.toArray(new Filter[mFilters.size()])); + + // If created a new bitmap, recycle it + if (scaledBitmap != mBitmap) { + scaledBitmap.recycle(); + } + swatches = quantizer.getQuantizedColors(); + + if (logger != null) { + logger.addSplit("Color quantization completed"); + } + } else { + // Else we're using the provided swatches + swatches = mSwatches; + } + + // If we haven't been provided with a generator, use the default + if (mGenerator == null) { + mGenerator = new DefaultGenerator(); + } + + // Now call let the Generator do it's thing + mGenerator.generate(swatches); + + if (logger != null) { + logger.addSplit("Generator.generate() completed"); + } + + // Now create a Palette instance + Palette p = new Palette(swatches, mGenerator); + + if (logger != null) { + logger.addSplit("Created Palette"); + logger.dumpToLog(); + } + + return p; + } + + /** + * Generate the {@link Palette} asynchronously. The provided listener's + * {@link PaletteAsyncListener#onGenerated} method will be called with the palette when + * generated. + */ + public AsyncTask<Bitmap, Void, Palette> generate(final PaletteAsyncListener listener) { + if (listener == null) { + throw new IllegalArgumentException("listener can not be null"); + } + + AsyncTask<Bitmap, Void, Palette> task = new AsyncTask<Bitmap, Void, Palette>() { + @Override + protected Palette doInBackground(Bitmap... params) { + return generate(); + } + + @Override + protected void onPostExecute(Palette colorExtractor) { + listener.onGenerated(colorExtractor); + } + }; + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mBitmap); + return task; + } + } + + static abstract class Generator { + + /** + * This method will be called with the {@link Palette.Swatch} that represent an image. + * You should process this list so that you have appropriate values when the other methods in + * class are called. + * <p> + * This method will probably be called on a background thread. + */ + public abstract void generate(List<Palette.Swatch> swatches); + + /** + * Return the most vibrant {@link Palette.Swatch} + */ + public Palette.Swatch getVibrantSwatch() { + return null; + } + + /** + * Return a light and vibrant {@link Palette.Swatch} + */ + public Palette.Swatch getLightVibrantSwatch() { + return null; + } + + /** + * Return a dark and vibrant {@link Palette.Swatch} + */ + public Palette.Swatch getDarkVibrantSwatch() { + return null; + } + + /** + * Return a muted {@link Palette.Swatch} + */ + public Palette.Swatch getMutedSwatch() { + return null; + } + + /** + * Return a muted and light {@link Palette.Swatch} + */ + public Palette.Swatch getLightMutedSwatch() { + return null; + } + + /** + * Return a muted and dark {@link Palette.Swatch} + */ + public Palette.Swatch getDarkMutedSwatch() { + return null; + } + } + + /** + * A Filter provides a mechanism for exercising fine-grained control over which colors + * are valid within a resulting {@link Palette}. + */ + public interface Filter { + /** + * Hook to allow clients to be able filter colors from resulting palette. + * + * @param rgb the color in RGB888. + * @param hsl HSL representation of the color. + * + * @return true if the color is allowed, false if not. + * + * @see Builder#addFilter(Filter) + */ + boolean isAllowed(int rgb, float[] hsl); + } + + /** + * The default filter. + */ + private static final Filter DEFAULT_FILTER = new Filter() { + private static final float BLACK_MAX_LIGHTNESS = 0.05f; + private static final float WHITE_MIN_LIGHTNESS = 0.95f; + + @Override + public boolean isAllowed(int rgb, float[] hsl) { + return !isWhite(hsl) && !isBlack(hsl) && !isNearRedILine(hsl); + } + + /** + * @return true if the color represents a color which is close to black. + */ + private boolean isBlack(float[] hslColor) { + return hslColor[2] <= BLACK_MAX_LIGHTNESS; + } + + /** + * @return true if the color represents a color which is close to white. + */ + private boolean isWhite(float[] hslColor) { + return hslColor[2] >= WHITE_MIN_LIGHTNESS; + } + + /** + * @return true if the color lies close to the red side of the I line. + */ + private boolean isNearRedILine(float[] hslColor) { + return hslColor[0] >= 10f && hslColor[0] <= 37f && hslColor[1] <= 0.82f; + } + }; +} diff --git a/core/java/com/android/internal/view/RotationPolicy.java b/core/java/com/android/internal/view/RotationPolicy.java index b479cb1..9b4910e 100644 --- a/core/java/com/android/internal/view/RotationPolicy.java +++ b/core/java/com/android/internal/view/RotationPolicy.java @@ -25,6 +25,7 @@ import android.net.Uri; import android.os.AsyncTask; import android.os.Handler; import android.os.RemoteException; +import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; import android.util.Log; @@ -41,7 +42,8 @@ import com.android.internal.R; public final class RotationPolicy { private static final String TAG = "RotationPolicy"; private static final int CURRENT_ROTATION = -1; - private static final int NATURAL_ROTATION = Surface.ROTATION_0; + private static final int NATURAL_ROTATION = + SystemProperties.getInt("persist.panel.orientation", Surface.ROTATION_0) / 90; private RotationPolicy() { } @@ -72,7 +74,7 @@ public final class RotationPolicy { * otherwise Configuration.ORIENTATION_UNDEFINED if any orientation is lockable. */ public static int getRotationLockOrientation(Context context) { - if (!areAllRotationsAllowed(context)) { + if (!isCurrentRotationAllowed(context)) { final Point size = new Point(); final IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); try { @@ -112,7 +114,8 @@ public final class RotationPolicy { Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, 0, UserHandle.USER_CURRENT); - final int rotation = areAllRotationsAllowed(context) ? CURRENT_ROTATION : NATURAL_ROTATION; + final int rotation = isCurrentRotationAllowed(context) + ? CURRENT_ROTATION : NATURAL_ROTATION; setRotationLock(enabled, rotation); } @@ -129,8 +132,39 @@ public final class RotationPolicy { setRotationLock(enabled, NATURAL_ROTATION); } - private static boolean areAllRotationsAllowed(Context context) { - return context.getResources().getBoolean(R.bool.config_allowAllRotations); + public static boolean isRotationAllowed(int rotation, + int userRotationAngles, boolean allowAllRotations) { + if (userRotationAngles < 0) { + // Not set by user so use these defaults + userRotationAngles = allowAllRotations ? + (1 | 2 | 4 | 8) : // All angles + (1 | 2 | 8); // All except 180 + } + switch (rotation) { + case Surface.ROTATION_0: + return (userRotationAngles & 1) != 0; + case Surface.ROTATION_90: + return (userRotationAngles & 2) != 0; + case Surface.ROTATION_180: + return (userRotationAngles & 4) != 0; + case Surface.ROTATION_270: + return (userRotationAngles & 8) != 0; + } + return false; + } + + private static boolean isCurrentRotationAllowed(Context context) { + int userRotationAngles = Settings.System.getInt(context.getContentResolver(), + Settings.System.ACCELEROMETER_ROTATION_ANGLES, -1); + boolean allowAllRotations = context.getResources().getBoolean( + com.android.internal.R.bool.config_allowAllRotations); + final IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); + try { + return isRotationAllowed(wm.getRotation(), userRotationAngles, allowAllRotations); + } catch (RemoteException exc) { + Log.w(TAG, "Unable to getWindowManagerService.getRotation()"); + } + return false; } private static void setRotationLock(final boolean enabled, final int rotation) { @@ -194,4 +228,4 @@ public final class RotationPolicy { public abstract void onChange(); } -}
\ No newline at end of file +} diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index 4e4552d..cc951a5 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -33,10 +33,12 @@ interface ILockSettings { void setLockPassword(in String password, in String savedPassword, int userId); VerifyCredentialResponse checkPassword(in String password, int userId); VerifyCredentialResponse verifyPassword(in String password, long challenge, int userId); + byte getLockPatternSize(int userId); boolean checkVoldPassword(int userId); boolean havePattern(int userId); boolean havePassword(int userId); void registerStrongAuthTracker(in IStrongAuthTracker tracker); void unregisterStrongAuthTracker(in IStrongAuthTracker tracker); void requireStrongAuth(int strongAuthReason, int userId); + void sanitizePassword(); } diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 60380fb..dc2cf1e 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -64,6 +64,11 @@ public class LockPatternUtils { private static final boolean DEBUG = false; /** + * The key to identify when the lock pattern enabled flag is being acccessed for legacy reasons. + */ + public static final String LEGACY_LOCK_PATTERN_ENABLED = "legacy_lock_pattern_enabled"; + + /** * The number of incorrect attempts before which we fall back on an alternative * method of verifying the user, and resetting their lock pattern. */ @@ -91,6 +96,11 @@ public class LockPatternUtils { */ public static final int MIN_LOCK_PASSWORD_SIZE = 4; + /* + * The default size of the pattern lockscreen. Ex: 3x3 + */ + public static final byte PATTERN_SIZE_DEFAULT = 3; + /** * The minimum number of dots the user must include in a wrong pattern * attempt for it to be counted against the counts that affect @@ -253,7 +263,7 @@ public class LockPatternUtils { throws RequestThrottledException { try { VerifyCredentialResponse response = - getLockSettings().verifyPattern(patternToString(pattern), challenge, userId); + getLockSettings().verifyPattern(patternToString(pattern, userId), challenge, userId); if (response == null) { // Shouldn't happen return null; @@ -281,7 +291,7 @@ public class LockPatternUtils { throws RequestThrottledException { try { VerifyCredentialResponse response = - getLockSettings().checkPattern(patternToString(pattern), userId); + getLockSettings().checkPattern(patternToString(pattern, userId), userId); if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { return true; @@ -508,7 +518,7 @@ public class LockPatternUtils { + MIN_LOCK_PATTERN_SIZE + " dots long."); } - getLockSettings().setLockPattern(patternToString(pattern), savedPattern, userId); + getLockSettings().setLockPattern(patternToString(pattern, userId), savedPattern, userId); DevicePolicyManager dpm = getDevicePolicyManager(); // Update the device encryption password. @@ -517,7 +527,7 @@ public class LockPatternUtils { if (!shouldEncryptWithCredentials(true)) { clearEncryptionPassword(); } else { - String stringPattern = patternToString(pattern); + String stringPattern = patternToString(pattern, userId); updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, stringPattern); } } @@ -533,6 +543,17 @@ public class LockPatternUtils { } } + /** + * clears stored password. + */ + public void sanitizePassword() { + try { + getLockSettings().sanitizePassword(); + } catch (RemoteException re) { + Log.e(TAG, "Couldn't sanitize password" + re); + } + } + private void updateCryptoUserInfo(int userId) { if (userId != UserHandle.USER_OWNER) { return; @@ -836,17 +857,18 @@ public class LockPatternUtils { * @param string The pattern serialized with {@link #patternToString} * @return The pattern. */ - public static List<LockPatternView.Cell> stringToPattern(String string) { + public static List<LockPatternView.Cell> stringToPattern(String string, byte gridSize) { if (string == null) { return null; } - List<LockPatternView.Cell> result = Lists.newArrayList(); + LockPatternView.Cell.updateSize(gridSize); + final byte[] bytes = string.getBytes(); for (int i = 0; i < bytes.length; i++) { byte b = (byte) (bytes[i] - '1'); - result.add(LockPatternView.Cell.of(b / 3, b % 3)); + result.add(LockPatternView.Cell.of(b / gridSize, b % gridSize, gridSize)); } return result; } @@ -856,16 +878,26 @@ public class LockPatternUtils { * @param pattern The pattern. * @return The pattern in string form. */ - public static String patternToString(List<LockPatternView.Cell> pattern) { + public String patternToString(List<LockPatternView.Cell> pattern, int userId) { + return patternToString(pattern, getLockPatternSize(userId)); + } + + /** + * Serialize a pattern. + * @param pattern The pattern. + * @return The pattern in string form. + */ + public static String patternToString(List<LockPatternView.Cell> pattern, byte gridSize) { if (pattern == null) { return ""; } final int patternSize = pattern.size(); + LockPatternView.Cell.updateSize(gridSize); byte[] res = new byte[patternSize]; for (int i = 0; i < patternSize; i++) { LockPatternView.Cell cell = pattern.get(i); - res[i] = (byte) (cell.getRow() * 3 + cell.getColumn() + '1'); + res[i] = (byte) (cell.getRow() * gridSize + cell.getColumn() + '1'); } return new String(res); } @@ -875,7 +907,6 @@ public class LockPatternUtils { return ""; } final int patternSize = pattern.length(); - byte[] res = new byte[patternSize]; final byte[] bytes = pattern.getBytes(); for (int i = 0; i < patternSize; i++) { @@ -891,7 +922,7 @@ public class LockPatternUtils { * @param pattern the gesture pattern. * @return the hash of the pattern in a byte array. */ - public static byte[] patternToHash(List<LockPatternView.Cell> pattern) { + public static byte[] patternToHash(List<LockPatternView.Cell> pattern, byte gridSize) { if (pattern == null) { return null; } @@ -900,7 +931,7 @@ public class LockPatternUtils { byte[] res = new byte[patternSize]; for (int i = 0; i < patternSize; i++) { LockPatternView.Cell cell = pattern.get(i); - res[i] = (byte) (cell.getRow() * 3 + cell.getColumn()); + res[i] = (byte) (cell.getRow() * gridSize + cell.getColumn()); } try { MessageDigest md = MessageDigest.getInstance("SHA-1"); @@ -985,6 +1016,19 @@ public class LockPatternUtils { return isLockPatternEnabled(getKeyguardStoredPasswordQuality(userId), userId); } + @Deprecated + public boolean isLegacyLockPatternEnabled(int userId) { + // Note: this value should default to {@code true} to avoid any reset that might result. + // We must use a special key to read this value, since it will by default return the value + // based on the new logic. + return getBoolean(LEGACY_LOCK_PATTERN_ENABLED, true, userId); + } + + @Deprecated + public void setLegacyLockPatternEnabled(int userId) { + setBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, true, userId); + } + private boolean isLockPatternEnabled(int mode, int userId) { return mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING && savedPatternExists(userId); @@ -1054,6 +1098,40 @@ public class LockPatternUtils { } /** + * @return the pattern lockscreen size + */ + public byte getLockPatternSize(int userId) { + long size = getLong(Settings.Secure.LOCK_PATTERN_SIZE, -1, userId); + if (size > 0 && size < 128) { + return (byte) size; + } + return LockPatternUtils.PATTERN_SIZE_DEFAULT; + } + + /** + * Set the pattern lockscreen size + */ + public void setLockPatternSize(long size, int userId) { + setLong(Settings.Secure.LOCK_PATTERN_SIZE, size, userId); + } + + public void setVisibleDotsEnabled(boolean enabled, int userId) { + setBoolean(Settings.Secure.LOCK_DOTS_VISIBLE, enabled, userId); + } + + public boolean isVisibleDotsEnabled(int userId) { + return getBoolean(Settings.Secure.LOCK_DOTS_VISIBLE, true, userId); + } + + public void setShowErrorPath(boolean enabled, int userId) { + setBoolean(Settings.Secure.LOCK_SHOW_ERROR_PATH, enabled, userId); + } + + public boolean isShowErrorPath(int userId) { + return getBoolean(Settings.Secure.LOCK_SHOW_ERROR_PATH, true, userId); + } + + /** * Set and store the lockout deadline, meaning the user can't attempt his/her unlock * pattern until the deadline has passed. * @return the chosen deadline. @@ -1090,7 +1168,7 @@ public class LockPatternUtils { return deadline; } - private boolean getBoolean(String secureSettingKey, boolean defaultValue, int userId) { + protected boolean getBoolean(String secureSettingKey, boolean defaultValue, int userId) { try { return getLockSettings().getBoolean(secureSettingKey, defaultValue, userId); } catch (RemoteException re) { @@ -1098,7 +1176,7 @@ public class LockPatternUtils { } } - private void setBoolean(String secureSettingKey, boolean enabled, int userId) { + protected void setBoolean(String secureSettingKey, boolean enabled, int userId) { try { getLockSettings().setBoolean(secureSettingKey, enabled, userId); } catch (RemoteException re) { @@ -1107,7 +1185,7 @@ public class LockPatternUtils { } } - private long getLong(String secureSettingKey, long defaultValue, int userHandle) { + protected long getLong(String secureSettingKey, long defaultValue, int userHandle) { try { return getLockSettings().getLong(secureSettingKey, defaultValue, userHandle); } catch (RemoteException re) { @@ -1115,7 +1193,7 @@ public class LockPatternUtils { } } - private void setLong(String secureSettingKey, long value, int userHandle) { + protected void setLong(String secureSettingKey, long value, int userHandle) { try { getLockSettings().setLong(secureSettingKey, value, userHandle); } catch (RemoteException re) { @@ -1124,7 +1202,7 @@ public class LockPatternUtils { } } - private String getString(String secureSettingKey, int userHandle) { + protected String getString(String secureSettingKey, int userHandle) { try { return getLockSettings().getString(secureSettingKey, null, userHandle); } catch (RemoteException re) { @@ -1132,7 +1210,7 @@ public class LockPatternUtils { } } - private void setString(String secureSettingKey, String value, int userHandle) { + protected void setString(String secureSettingKey, String value, int userHandle) { try { getLockSettings().setString(secureSettingKey, value, userHandle); } catch (RemoteException re) { diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java index 9211eaa..baa228f 100644 --- a/core/java/com/android/internal/widget/LockPatternView.java +++ b/core/java/com/android/internal/widget/LockPatternView.java @@ -51,6 +51,7 @@ import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import com.android.internal.R; +import com.android.internal.widget.LockPatternUtils; import java.util.ArrayList; import java.util.HashMap; @@ -58,7 +59,7 @@ import java.util.List; /** * Displays and detects the user's unlock attempt, which is a drag of a finger - * across 9 regions of the screen. + * across regions of the screen. * * Is also capable of displaying a static pattern in "in progress", "wrong" or * "correct" states. @@ -70,7 +71,7 @@ public class LockPatternView extends View { private static final int ASPECT_LOCK_HEIGHT = 2; // Fixed height; width will be minimum of (w,h) private static final boolean PROFILE_DRAWING = false; - private final CellState[][] mCellStates; + private CellState[][] mCellStates; private final int mDotSize; private final int mDotSizeActivated; @@ -88,6 +89,8 @@ public class LockPatternView extends View { */ private static final int MILLIS_PER_CIRCLE_ANIMATING = 700; + private byte mPatternSize = LockPatternUtils.PATTERN_SIZE_DEFAULT; + /** * This can be used to avoid updating the display for very small motions or noisy panels. * It didn't seem to have much impact on the devices tested, so currently set to 0. @@ -98,7 +101,7 @@ public class LockPatternView extends View { private static final String TAG = "LockPatternView"; private OnPatternListener mOnPatternListener; - private final ArrayList<Cell> mPattern = new ArrayList<Cell>(9); + private ArrayList<Cell> mPattern = new ArrayList<Cell>(mPatternSize * mPatternSize); /** * Lookup table for the circles of the pattern we are currently drawing. @@ -106,7 +109,7 @@ public class LockPatternView extends View { * in which case we use this to hold the cells we are drawing for the in * progress animation. */ - private final boolean[][] mPatternDrawLookup = new boolean[3][3]; + private boolean[][] mPatternDrawLookup = new boolean[mPatternSize][mPatternSize]; /** * the in progress point: @@ -123,6 +126,8 @@ public class LockPatternView extends View { private boolean mInStealthMode = false; private boolean mEnableHapticFeedback = true; private boolean mPatternInProgress = false; + private boolean mVisibleDots = true; + private boolean mShowErrorPath = true; private float mHitFactor = 0.6f; @@ -143,32 +148,26 @@ public class LockPatternView extends View { private PatternExploreByTouchHelper mExploreByTouchHelper; private AudioManager mAudioManager; + private LockPatternUtils mLockPatternUtils; + /** - * Represents a cell in the 3 X 3 matrix of the unlock pattern view. + * Represents a cell in the matrix of the unlock pattern view. */ public static final class Cell { final int row; final int column; - // keep # objects limited to 9 - private static final Cell[][] sCells = createCells(); - - private static Cell[][] createCells() { - Cell[][] res = new Cell[3][3]; - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - res[i][j] = new Cell(i, j); - } - } - return res; + static Cell[][] sCells; + static { + updateSize(LockPatternUtils.PATTERN_SIZE_DEFAULT); } /** * @param row The row of the cell. * @param column The column of the cell. */ - private Cell(int row, int column) { - checkRange(row, column); + private Cell(int row, int column, byte size) { + checkRange(row, column, size); this.row = row; this.column = column; } @@ -181,17 +180,30 @@ public class LockPatternView extends View { return column; } - public static Cell of(int row, int column) { - checkRange(row, column); + /** + * @param row The row of the cell. + * @param column The column of the cell. + */ + public static synchronized Cell of(int row, int column, byte size) { + checkRange(row, column, size); return sCells[row][column]; } - private static void checkRange(int row, int column) { - if (row < 0 || row > 2) { - throw new IllegalArgumentException("row must be in range 0-2"); + public static void updateSize(byte size) { + sCells = new Cell[size][size]; + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + sCells[i][j] = new Cell(i, j, size); + } } - if (column < 0 || column > 2) { - throw new IllegalArgumentException("column must be in range 0-2"); + } + + private static void checkRange(int row, int column, byte size) { + if (row < 0 || row > size - 1) { + throw new IllegalArgumentException("row must be in range 0-" + (size - 1)); + } + if (column < 0 || column > size - 1) { + throw new IllegalArgumentException("column must be in range 0-" + (size - 1)); } } @@ -317,9 +329,9 @@ public class LockPatternView extends View { mPaint.setAntiAlias(true); mPaint.setDither(true); - mCellStates = new CellState[3][3]; - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { + mCellStates = new CellState[mPatternSize][mPatternSize]; + for (int i = 0; i < mPatternSize; i++) { + for (int j = 0; j < mPatternSize; j++) { mCellStates[i][j] = new CellState(); mCellStates[i][j].radius = mDotSize/2; mCellStates[i][j].row = i; @@ -355,6 +367,13 @@ public class LockPatternView extends View { } /** + * @return the current pattern lockscreen size. + */ + public byte getLockPatternSize() { + return mPatternSize; + } + + /** * Set whether the view is in stealth mode. If true, there will be no * visible feedback as the user enters the pattern. * @@ -364,6 +383,22 @@ public class LockPatternView extends View { mInStealthMode = inStealthMode; } + public void setVisibleDots(boolean visibleDots) { + mVisibleDots = visibleDots; + } + + public boolean isVisibleDots() { + return mVisibleDots; + } + + public void setShowErrorPath(boolean showErrorPath) { + mShowErrorPath = showErrorPath; + } + + public boolean isShowErrorPath() { + return mShowErrorPath; + } + /** * Set whether the view will use tactile feedback. If true, there will be * tactile feedback as the user enters the pattern. @@ -375,6 +410,35 @@ public class LockPatternView extends View { } /** + * Set the pattern size of the lockscreen + * + * @param size The pattern size. + */ + public void setLockPatternSize(byte size) { + mPatternSize = size; + Cell.updateSize(size); + mCellStates = new CellState[mPatternSize][mPatternSize]; + for (int i = 0; i < mPatternSize; i++) { + for (int j = 0; j < mPatternSize; j++) { + mCellStates[i][j] = new CellState(); + mCellStates[i][j].radius = mDotSize / 2; + mCellStates[i][j].row = i; + mCellStates[i][j].col = j; + } + } + mPattern = new ArrayList<Cell>(size * size); + mPatternDrawLookup = new boolean[size][size]; + } + + /** + * Set the LockPatternUtil instance used to encode a pattern to a string + * @param utils The instance. + */ + public void setLockPatternUtils(LockPatternUtils utils) { + mLockPatternUtils = utils; + } + + /** * Set the call back for pattern detection. * @param onPatternListener The call back. */ @@ -589,8 +653,8 @@ public class LockPatternView extends View { * Clear the pattern lookup table. */ private void clearPatternDrawLookup() { - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { + for (int i = 0; i < mPatternSize; i++) { + for (int j = 0; j < mPatternSize; j++) { mPatternDrawLookup[i][j] = false; } } @@ -614,11 +678,11 @@ public class LockPatternView extends View { @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { final int width = w - mPaddingLeft - mPaddingRight; - mSquareWidth = width / 3.0f; + mSquareWidth = width / (float) mPatternSize; if (DEBUG_A11Y) Log.v(TAG, "onSizeChanged(" + w + "," + h + ")"); final int height = h - mPaddingTop - mPaddingBottom; - mSquareHeight = height / 3.0f; + mSquareHeight = height / (float) mPatternSize; mExploreByTouchHelper.invalidateRoot(); } @@ -674,7 +738,6 @@ public class LockPatternView extends View { if (cell != null) { // check for gaps in existing pattern - Cell fillInGapCell = null; final ArrayList<Cell> pattern = mPattern; if (!pattern.isEmpty()) { final Cell lastCell = pattern.get(pattern.size() - 1); @@ -684,21 +747,19 @@ public class LockPatternView extends View { int fillInRow = lastCell.row; int fillInColumn = lastCell.column; - if (Math.abs(dRow) == 2 && Math.abs(dColumn) != 1) { - fillInRow = lastCell.row + ((dRow > 0) ? 1 : -1); - } - - if (Math.abs(dColumn) == 2 && Math.abs(dRow) != 1) { - fillInColumn = lastCell.column + ((dColumn > 0) ? 1 : -1); + if (dRow == 0 || dColumn == 0 || Math.abs(dRow) == Math.abs(dColumn)) { + while (true) { + fillInRow += Integer.signum(dRow); + fillInColumn += Integer.signum(dColumn); + if (fillInRow == cell.row && fillInColumn == cell.column) break; + Cell fillInGapCell = Cell.of(fillInRow, fillInColumn, mPatternSize); + if (!mPatternDrawLookup[fillInGapCell.row][fillInGapCell.column]) { + addCellToPattern(fillInGapCell); + } + } } - - fillInGapCell = Cell.of(fillInRow, fillInColumn); } - if (fillInGapCell != null && - !mPatternDrawLookup[fillInGapCell.row][fillInGapCell.column]) { - addCellToPattern(fillInGapCell); - } addCellToPattern(cell); if (mEnableHapticFeedback) { performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, @@ -796,7 +857,7 @@ public class LockPatternView extends View { if (mPatternDrawLookup[rowHit][columnHit]) { return null; } - return Cell.of(rowHit, columnHit); + return Cell.of(rowHit, columnHit, mPatternSize); } /** @@ -810,7 +871,7 @@ public class LockPatternView extends View { float hitSize = squareHeight * mHitFactor; float offset = mPaddingTop + (squareHeight - hitSize) / 2f; - for (int i = 0; i < 3; i++) { + for (int i = 0; i < mPatternSize; i++) { final float hitTop = offset + squareHeight * i; if (y >= hitTop && y <= hitTop + hitSize) { @@ -830,7 +891,7 @@ public class LockPatternView extends View { float hitSize = squareWidth * mHitFactor; float offset = mPaddingLeft + (squareWidth - hitSize) / 2f; - for (int i = 0; i < 3; i++) { + for (int i = 0; i < mPatternSize; i++) { final float hitLeft = offset + squareWidth * i; if (x >= hitLeft && x <= hitLeft + hitSize) { @@ -984,8 +1045,8 @@ public class LockPatternView extends View { } private void cancelLineAnimations() { - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { + for (int i = 0; i < mPatternSize; i++) { + for (int j = 0; j < mPatternSize; j++) { CellState state = mCellStates[i][j]; if (state.lineAnimator != null) { state.lineAnimator.cancel(); @@ -1088,20 +1149,21 @@ public class LockPatternView extends View { currentPath.rewind(); // draw the circles - for (int i = 0; i < 3; i++) { - float centerY = getCenterYForRow(i); - for (int j = 0; j < 3; j++) { - CellState cellState = mCellStates[i][j]; - float centerX = getCenterXForColumn(j); - float translationY = cellState.translationY; - if (isHardwareAccelerated() && cellState.hwAnimating) { - DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas; - displayListCanvas.drawCircle(cellState.hwCenterX, cellState.hwCenterY, - cellState.hwRadius, cellState.hwPaint); - } else { - drawCircle(canvas, (int) centerX, (int) centerY + translationY, - cellState.radius, drawLookup[i][j], cellState.alpha); - + if (mVisibleDots) { + for (int i = 0; i < mPatternSize; i++) { + float centerY = getCenterYForRow(i); + for (int j = 0; j < mPatternSize; j++) { + CellState cellState = mCellStates[i][j]; + float centerX = getCenterXForColumn(j); + float translationY = cellState.translationY; + if (isHardwareAccelerated() && cellState.hwAnimating) { + DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas; + displayListCanvas.drawCircle(cellState.hwCenterX, cellState.hwCenterY, + cellState.hwRadius, cellState.hwPaint); + } else { + drawCircle(canvas, (int) centerX, (int) centerY + translationY, + cellState.radius, drawLookup[i][j], cellState.alpha); + } } } } @@ -1109,8 +1171,8 @@ public class LockPatternView extends View { // TODO: the path should be created and cached every time we hit-detect a cell // only the last segment of the path should be computed here // draw the path of the pattern (unless we are in stealth mode) - final boolean drawPath = !mInStealthMode; - + final boolean drawPath = ((!mInStealthMode && mPatternDisplayMode != DisplayMode.Wrong) + || (mPatternDisplayMode == DisplayMode.Wrong && mShowErrorPath)); if (drawPath) { mPathPaint.setColor(getCurrentColor(true /* partOfPattern */)); @@ -1168,7 +1230,9 @@ public class LockPatternView extends View { } private int getCurrentColor(boolean partOfPattern) { - if (!partOfPattern || mInStealthMode || mPatternInProgress) { + if (!partOfPattern || (mInStealthMode && mPatternDisplayMode != DisplayMode.Wrong) + || (mPatternDisplayMode == DisplayMode.Wrong && !mShowErrorPath) + || mPatternInProgress) { // unselected circle return mRegularColor; } else if (mPatternDisplayMode == DisplayMode.Wrong) { @@ -1196,9 +1260,9 @@ public class LockPatternView extends View { protected Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); return new SavedState(superState, - LockPatternUtils.patternToString(mPattern), - mPatternDisplayMode.ordinal(), - mInputEnabled, mInStealthMode, mEnableHapticFeedback); + LockPatternUtils.patternToString(mPattern, mPatternSize), + mPatternDisplayMode.ordinal(), mPatternSize, + mInputEnabled, mInStealthMode, mEnableHapticFeedback, mVisibleDots, mShowErrorPath); } @Override @@ -1207,11 +1271,14 @@ public class LockPatternView extends View { super.onRestoreInstanceState(ss.getSuperState()); setPattern( DisplayMode.Correct, - LockPatternUtils.stringToPattern(ss.getSerializedPattern())); + LockPatternUtils.stringToPattern(ss.getSerializedPattern(), ss.getPatternSize())); mPatternDisplayMode = DisplayMode.values()[ss.getDisplayMode()]; + mPatternSize = ss.getPatternSize(); mInputEnabled = ss.isInputEnabled(); mInStealthMode = ss.isInStealthMode(); mEnableHapticFeedback = ss.isTactileFeedbackEnabled(); + mVisibleDots = ss.isVisibleDots(); + mShowErrorPath = ss.isShowErrorPath(); } /** @@ -1221,21 +1288,28 @@ public class LockPatternView extends View { private final String mSerializedPattern; private final int mDisplayMode; + private final byte mPatternSize; private final boolean mInputEnabled; private final boolean mInStealthMode; private final boolean mTactileFeedbackEnabled; + private final boolean mVisibleDots; + private final boolean mShowErrorPath; /** * Constructor called from {@link LockPatternView#onSaveInstanceState()} */ private SavedState(Parcelable superState, String serializedPattern, int displayMode, - boolean inputEnabled, boolean inStealthMode, boolean tactileFeedbackEnabled) { + byte patternSize, boolean inputEnabled, boolean inStealthMode, + boolean tactileFeedbackEnabled, boolean visibleDots, boolean showErrorPath) { super(superState); mSerializedPattern = serializedPattern; mDisplayMode = displayMode; + mPatternSize = patternSize; mInputEnabled = inputEnabled; mInStealthMode = inStealthMode; mTactileFeedbackEnabled = tactileFeedbackEnabled; + mVisibleDots = visibleDots; + mShowErrorPath = showErrorPath; } /** @@ -1245,9 +1319,12 @@ public class LockPatternView extends View { super(in); mSerializedPattern = in.readString(); mDisplayMode = in.readInt(); + mPatternSize = (byte) in.readByte(); mInputEnabled = (Boolean) in.readValue(null); mInStealthMode = (Boolean) in.readValue(null); mTactileFeedbackEnabled = (Boolean) in.readValue(null); + mVisibleDots = (Boolean) in.readValue(null); + mShowErrorPath = (Boolean) in.readValue(null); } public String getSerializedPattern() { @@ -1258,6 +1335,10 @@ public class LockPatternView extends View { return mDisplayMode; } + public byte getPatternSize() { + return mPatternSize; + } + public boolean isInputEnabled() { return mInputEnabled; } @@ -1270,14 +1351,25 @@ public class LockPatternView extends View { return mTactileFeedbackEnabled; } + public boolean isVisibleDots() { + return mVisibleDots; + } + + public boolean isShowErrorPath() { + return mShowErrorPath; + } + @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeString(mSerializedPattern); dest.writeInt(mDisplayMode); + dest.writeByte(mPatternSize); dest.writeValue(mInputEnabled); dest.writeValue(mInStealthMode); dest.writeValue(mTactileFeedbackEnabled); + dest.writeValue(mVisibleDots); + dest.writeValue(mShowErrorPath); } @SuppressWarnings({ "unused", "hiding" }) // Found using reflection diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java index 35ed63b..d88f479 100644 --- a/core/java/com/android/internal/widget/SwipeDismissLayout.java +++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java @@ -89,14 +89,21 @@ public class SwipeDismissLayout extends FrameLayout { } }; private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() { + private Runnable mRunnable = new Runnable() { + @Override + public void run() { + if (mDismissed) { + dismiss(); + } else { + cancel(); + } + resetMembers(); + } + }; + @Override public void onReceive(Context context, Intent intent) { - if (mDismissed) { - dismiss(); - } else { - cancel(); - } - resetMembers(); + post(mRunnable); } }; private IntentFilter mScreenOffFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF); diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java index 92d5aea..6103ebc 100644 --- a/core/java/com/android/server/BootReceiver.java +++ b/core/java/com/android/server/BootReceiver.java @@ -97,6 +97,7 @@ public class BootReceiver extends BroadcastReceiver { final DropBoxManager db = (DropBoxManager) ctx.getSystemService(Context.DROPBOX_SERVICE); final SharedPreferences prefs = ctx.getSharedPreferences("log_files", Context.MODE_PRIVATE); final String headers = new StringBuilder(512) + .append("CM Version: ").append(SystemProperties.get("ro.cm.version")).append("\n") .append("Build: ").append(Build.FINGERPRINT).append("\n") .append("Hardware: ").append(Build.BOARD).append("\n") .append("Revision: ") diff --git a/core/java/com/android/server/net/BaseNetworkObserver.java b/core/java/com/android/server/net/BaseNetworkObserver.java index 3d9fb5c..c3dcd40 100644 --- a/core/java/com/android/server/net/BaseNetworkObserver.java +++ b/core/java/com/android/server/net/BaseNetworkObserver.java @@ -63,6 +63,11 @@ public class BaseNetworkObserver extends INetworkManagementEventObserver.Stub { } @Override + public void interfaceMessageRecevied(String message) { + // default no-op + } + + @Override public void limitReached(String limitName, String iface) { // default no-op } diff --git a/core/java/org/codeaurora/camera/Android.mk b/core/java/org/codeaurora/camera/Android.mk new file mode 100644 index 0000000..2e005f7 --- /dev/null +++ b/core/java/org/codeaurora/camera/Android.mk @@ -0,0 +1,30 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_MODULE_TAGS := optional + +LOCAL_MODULE:= org.codeaurora.camera + +# This will install the file in /system/framework +LOCAL_MODULE_PATH := $(TARGET_OUT_JAVA_LIBRARIES) + +include $(BUILD_JAVA_LIBRARY) + +# ==== permissions ======================== +include $(CLEAR_VARS) + +LOCAL_MODULE := org.codeaurora.camera.xml + +LOCAL_MODULE_TAGS := optional + +LOCAL_MODULE_CLASS := ETC + +# This will install the file in /system/etc/permissions +LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions + +LOCAL_SRC_FILES := $(LOCAL_MODULE) + +include $(BUILD_PREBUILT) diff --git a/core/java/org/codeaurora/camera/ExtendedFace.java b/core/java/org/codeaurora/camera/ExtendedFace.java new file mode 100644 index 0000000..afda0b6 --- /dev/null +++ b/core/java/org/codeaurora/camera/ExtendedFace.java @@ -0,0 +1,211 @@ +/* Copyright (c) 2015, The Linux Foundataion. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are +* met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided +* with the distribution. +* * Neither the name of The Linux Foundation nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ + +package org.codeaurora.camera; + +import android.hardware.Camera; + +import java.util.ArrayList; + +import android.os.Bundle; + +import android.os.SystemProperties; + +/** + * {@hide} Information about a face identified through Extended camera face + * + * <p> + * When face detection is used with a camera, the {@link FaceDetectionListener} + * returns a list of face objects for use in focusing and metering. + * </p> + * + * @see FaceDetectionListener + */ +public class ExtendedFace extends android.hardware.Camera.Face { + public ExtendedFace() { + super(); + } + + private int smileDegree = 0; + private int smileScore = 0; + private int blinkDetected = 0; + private int faceRecognized = 0; + private int gazeAngle = 0; + private int updownDir = 0; + private int leftrightDir = 0; + private int rollDir = 0; + private int leyeBlink = 0; + private int reyeBlink = 0; + private int leftrightGaze = 0; + private int topbottomGaze = 0; + + private static final String STR_TRUE = "true"; + private static final String STR_FALSE = "false"; + + /** + * The smilie degree for the detection of the face. + * + * @see #startFaceDetection() + */ + public int getSmileDegree() { + return smileDegree; + } + + /** + * The smilie score for the detection of the face. + * + * @see #startFaceDetection() + */ + public int getSmileScore() { + return smileScore; + } + + /** + * The smilie degree for the detection of the face. + * + * @see #startFaceDetection() + */ + public int getBlinkDetected() { + return blinkDetected; + } + + /** + * If face is recognized. + * + * @see #startFaceDetection() + */ + public int getFaceRecognized() { + return faceRecognized; + } + + /** + * The gaze angle for the detected face. + * + * @see #startFaceDetection() + */ + public int getGazeAngle() { + return gazeAngle; + } + + /** + * The up down direction for the detected face. + * + * @see #startFaceDetection() + */ + public int getUpDownDirection() { + return updownDir; + } + + /** + * The left right direction for the detected face. + * + * @see #startFaceDetection() + */ + public int getLeftRightDirection() { + return leftrightDir; + } + + /** + * The roll direction for the detected face. + * + * @see #startFaceDetection() + */ + public int getRollDirection() { + return rollDir; + } + + /** + * The degree of left eye blink for the detected face. + * + * @see #startFaceDetection() + */ + public int getLeftEyeBlinkDegree() { + return leyeBlink; + } + + /** + * The degree of right eye blink for the detected face. + * + * @see #startFaceDetection() + */ + public int getRightEyeBlinkDegree() { + return reyeBlink; + } + + /** + * The gaze degree of left-right direction for the detected face. + * + * @see #startFaceDetection() + */ + public int getLeftRightGazeDegree() { + return leftrightGaze; + } + + /** + * The gaze degree of up-down direction for the detected face. + * + * @see #startFaceDetection() + */ + public int getTopBottomGazeDegree() { + return topbottomGaze; + } + + private static final String BUNDLE_KEY_SMILE_SCORE = "smileScore"; + private static final String BUNDLE_KEY_SMILE_VALUE = "smileValue"; + private static final String BUNDLE_KEY_BLINK_DETECTED = "blinkDetected"; + private static final String BUNDLE_KEY_LEFT_EYE_CLOSED_VALUE = "leftEyeClosedValue"; + private static final String BUNDLE_KEY_RIGHT_EYE_CLOSED_VALUE = "rightEyeClosedValue"; + private static final String BUNDLE_KEY_FACE_PITCH_DEGREE = "facePitchDegree"; + private static final String BUNDLE_KEY_FACE_YAW_DEGREE = "faceYawDegree"; + private static final String BUNDLE_KEY_FACE_ROLL_DEGREE = "faceRollDegree"; + private static final String BUNDLE_KEY_GAZE_UP_DOWN_DEGREE = "gazeUpDownDegree"; + private static final String BUNDLE_KEY_GAZE_LEFT_RIGHT_DEGREE = "gazeLeftRightDegree"; + private static final String BUNDLE_KEY_FACE_RECOGNIZED = "faceRecognized"; + + public Bundle getExtendedFaceInfo() { + Bundle faceInfo = new Bundle(); + faceInfo.putInt(BUNDLE_KEY_SMILE_VALUE, this.smileDegree); + + faceInfo.putInt(BUNDLE_KEY_LEFT_EYE_CLOSED_VALUE, this.leyeBlink); + faceInfo.putInt(BUNDLE_KEY_RIGHT_EYE_CLOSED_VALUE, this.reyeBlink); + + faceInfo.putInt(BUNDLE_KEY_FACE_PITCH_DEGREE, this.updownDir); + faceInfo.putInt(BUNDLE_KEY_FACE_YAW_DEGREE, this.leftrightDir); + faceInfo.putInt(BUNDLE_KEY_FACE_ROLL_DEGREE, this.rollDir); + faceInfo.putInt(BUNDLE_KEY_GAZE_UP_DOWN_DEGREE, this.topbottomGaze); + faceInfo.putInt(BUNDLE_KEY_GAZE_LEFT_RIGHT_DEGREE, this.leftrightGaze); + + faceInfo.putInt(BUNDLE_KEY_BLINK_DETECTED, this.blinkDetected); + faceInfo.putInt(BUNDLE_KEY_SMILE_SCORE, this.smileScore); + faceInfo.putInt(BUNDLE_KEY_FACE_RECOGNIZED, this.faceRecognized); + + return faceInfo; + } + +} diff --git a/core/java/org/codeaurora/camera/org.codeaurora.camera.xml b/core/java/org/codeaurora/camera/org.codeaurora.camera.xml new file mode 100644 index 0000000..20b2aa0 --- /dev/null +++ b/core/java/org/codeaurora/camera/org.codeaurora.camera.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (c) 2015, The Linux Foundation. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--> + +<!-- This is the library that allows devices to use Camera QFace APIs. --> +<permissions> + <library name="org.codeaurora.camera" + file="/system/framework/org.codeaurora.camera.jar" /> +</permissions> |