Merge "Add OpenGL backend to ImageWallpaper Bug #5204874"
diff --git a/Android.mk b/Android.mk
index fdf0933..d4dc088 100644
--- a/Android.mk
+++ b/Android.mk
@@ -183,7 +183,8 @@
media/java/android/media/IAudioFocusDispatcher.aidl \
media/java/android/media/IMediaScannerListener.aidl \
media/java/android/media/IMediaScannerService.aidl \
- media/java/android/media/IRemoteControlClientDispatcher.aidl \
+ media/java/android/media/IRemoteControlClient.aidl \
+ media/java/android/media/IRemoteControlDisplay.aidl \
telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \
telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \
telephony/java/com/android/internal/telephony/ITelephony.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 2593065..0dba18b 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -105,6 +105,8 @@
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/SystemUI_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/R/com/android/systemui/R.java)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/media/java/android/media/IAudioService.P)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/media/java/android/media/IAudioService.P)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
diff --git a/api/current.txt b/api/current.txt
index e432436..250f09f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -958,6 +958,8 @@
field public static final int textAppearanceLarge = 16842816; // 0x1010040
field public static final int textAppearanceLargeInverse = 16842819; // 0x1010043
field public static final int textAppearanceLargePopupMenu = 16843521; // 0x1010301
+ field public static final int textAppearanceListItem = 16843688; // 0x10103a8
+ field public static final int textAppearanceListItemSmall = 16843689; // 0x10103a9
field public static final int textAppearanceMedium = 16842817; // 0x1010041
field public static final int textAppearanceMediumInverse = 16842820; // 0x1010044
field public static final int textAppearanceSearchResultSubtitle = 16843424; // 0x10102a0
@@ -4224,6 +4226,7 @@
method public java.util.Set<android.bluetooth.BluetoothDevice> getBondedDevices();
method public static synchronized android.bluetooth.BluetoothAdapter getDefaultAdapter();
method public java.lang.String getName();
+ method public int getProfileConnectionState(int);
method public boolean getProfileProxy(android.content.Context, android.bluetooth.BluetoothProfile.ServiceListener, int);
method public android.bluetooth.BluetoothDevice getRemoteDevice(java.lang.String);
method public int getScanMode();
@@ -8718,7 +8721,6 @@
public class SurfaceTexture {
ctor public SurfaceTexture(int);
- ctor public SurfaceTexture(int, boolean);
method public long getTimestamp();
method public void getTransformMatrix(float[]);
method public void release();
@@ -9363,6 +9365,7 @@
field public static final int FOCUS_DISTANCE_NEAR_INDEX = 0; // 0x0
field public static final int FOCUS_DISTANCE_OPTIMAL_INDEX = 1; // 0x1
field public static final java.lang.String FOCUS_MODE_AUTO = "auto";
+ field public static final java.lang.String FOCUS_MODE_CONTINUOUS_PICTURE = "continuous-picture";
field public static final java.lang.String FOCUS_MODE_CONTINUOUS_VIDEO = "continuous-video";
field public static final java.lang.String FOCUS_MODE_EDOF = "edof";
field public static final java.lang.String FOCUS_MODE_FIXED = "fixed";
@@ -11292,9 +11295,33 @@
public class EffectFactory {
method public android.media.effect.Effect createEffect(java.lang.String);
method public static boolean isEffectSupported(java.lang.String);
- field public static final java.lang.String EFFECT_BRIGHTNESS = "BrightnessEffect";
- field public static final java.lang.String EFFECT_CONTRAST = "ContrastEffect";
- field public static final java.lang.String EFFECT_FISHEYE = "FisheyeEffect";
+ field public static final java.lang.String EFFECT_AUTOFIX = "android.media.effect.effects.AutoFixEffect";
+ field public static final java.lang.String EFFECT_BACKDROPPER = "android.media.effect.effects.BackDropperEffect";
+ field public static final java.lang.String EFFECT_BLACKWHITE = "android.media.effect.effects.BlackWhiteEffect";
+ field public static final java.lang.String EFFECT_BRIGHTNESS = "android.media.effect.effects.BrightnessEffect";
+ field public static final java.lang.String EFFECT_CONTRAST = "android.media.effect.effects.ContrastEffect";
+ field public static final java.lang.String EFFECT_CROP = "android.media.effect.effects.CropEffect";
+ field public static final java.lang.String EFFECT_CROSSPROCESS = "android.media.effect.effects.CrossProcessEffect";
+ field public static final java.lang.String EFFECT_DOCUMENTARY = "android.media.effect.effects.DocumentaryEffect";
+ field public static final java.lang.String EFFECT_DOODLE = "android.media.effect.effects.DoodleEffect";
+ field public static final java.lang.String EFFECT_DUOTONE = "android.media.effect.effects.DuotoneEffect";
+ field public static final java.lang.String EFFECT_FILLLIGHT = "android.media.effect.effects.FillLightEffect";
+ field public static final java.lang.String EFFECT_FISHEYE = "android.media.effect.effects.FisheyeEffect";
+ field public static final java.lang.String EFFECT_FLIP = "android.media.effect.effects.FlipEffect";
+ field public static final java.lang.String EFFECT_GRAIN = "android.media.effect.effects.GrainEffect";
+ field public static final java.lang.String EFFECT_GRAYSCALE = "android.media.effect.effects.GrayscaleEffect";
+ field public static final java.lang.String EFFECT_LOMOISH = "android.media.effect.effects.LomoishEffect";
+ field public static final java.lang.String EFFECT_NEGATIVE = "android.media.effect.effects.NegativeEffect";
+ field public static final java.lang.String EFFECT_POSTERIZE = "android.media.effect.effects.PosterizeEffect";
+ field public static final java.lang.String EFFECT_REDEYE = "android.media.effect.effects.RedEyeEffect";
+ field public static final java.lang.String EFFECT_ROTATE = "android.media.effect.effects.RotateEffect";
+ field public static final java.lang.String EFFECT_SATURATE = "android.media.effect.effects.SaturateEffect";
+ field public static final java.lang.String EFFECT_SEPIA = "android.media.effect.effects.SepiaEffect";
+ field public static final java.lang.String EFFECT_SHARPEN = "android.media.effect.effects.SharpenEffect";
+ field public static final java.lang.String EFFECT_STRAIGHTEN = "android.media.effect.effects.StraightenEffect";
+ field public static final java.lang.String EFFECT_TEMPERATURE = "android.media.effect.effects.ColorTemperatureEffect";
+ field public static final java.lang.String EFFECT_TINT = "android.media.effect.effects.TintEffect";
+ field public static final java.lang.String EFFECT_VIGNETTE = "android.media.effect.effects.VignetteEffect";
}
public abstract interface EffectUpdateListener {
@@ -18402,6 +18429,7 @@
public class WallpaperService.Engine {
ctor public WallpaperService.Engine();
+ method protected void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public int getDesiredMinimumHeight();
method public int getDesiredMinimumWidth();
method public android.view.SurfaceHolder getSurfaceHolder();
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index 836cf15..0b08c5e 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -80,6 +80,9 @@
dump_file("SLAB INFO", "/proc/slabinfo");
dump_file("ZONEINFO", "/proc/zoneinfo");
dump_file("PAGETYPEINFO", "/proc/pagetypeinfo");
+ dump_file("BUDDYINFO", "/proc/buddyinfo");
+ dump_file("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
+ run_command("QTAGUID STATS INFO", 10, "su", "root", "cat", "/proc/net/xt_qtaguid/stats", NULL);
if (screenshot_path[0]) {
LOGI("taking screenshot\n");
@@ -114,8 +117,10 @@
dump_file("NETWORK ROUTES", "/proc/net/route");
dump_file("NETWORK ROUTES IPV6", "/proc/net/ipv6_route");
dump_file("ARP CACHE", "/proc/net/arp");
- run_command("IPTABLES", 10, "su", "root", "iptables", "-L", "-n", NULL);
+ run_command("IPTABLES", 10, "su", "root", "iptables", "-L", "-nvx", NULL);
+ run_command("IP6TABLES", 10, "su", "root", "ip6tables", "-L", "-nvx", NULL);
run_command("IPTABLE NAT", 10, "su", "root", "iptables", "-t", "nat", "-L", "-n", NULL);
+ run_command("IPT6ABLE NAT", 10, "su", "root", "ip6tables", "-t", "nat", "-L", "-n", NULL);
run_command("WIFI NETWORKS", 20,
"su", "root", "wpa_cli", "list_networks", NULL);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index e376220..e3075d7 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1943,7 +1943,6 @@
// we are back active so skip it.
unscheduleGcIdler();
- Slog.i(TAG, "Launch: profileFd=" + r.profileFile + " stop=" + r.autoStopProfiler);
if (r.profileFd != null) {
mBoundApplication.setProfiler(r.profileFile, r.profileFd);
mBoundApplication.startProfiling();
@@ -4091,7 +4090,7 @@
final void removeDeadProvider(String name, IContentProvider provider) {
synchronized(mProviderMap) {
ProviderClientRecord pr = mProviderMap.get(name);
- if (pr.mProvider.asBinder() == provider.asBinder()) {
+ if (pr != null && pr.mProvider.asBinder() == provider.asBinder()) {
Slog.i(TAG, "Removing dead content provider: " + name);
ProviderClientRecord removed = mProviderMap.remove(name);
if (removed != null) {
@@ -4101,17 +4100,6 @@
}
}
- final void removeDeadProviderLocked(String name, IContentProvider provider) {
- ProviderClientRecord pr = mProviderMap.get(name);
- if (pr.mProvider.asBinder() == provider.asBinder()) {
- Slog.i(TAG, "Removing dead content provider: " + name);
- ProviderClientRecord removed = mProviderMap.remove(name);
- if (removed != null) {
- removed.mProvider.asBinder().unlinkToDeath(removed, 0);
- }
- }
- }
-
private IContentProvider installProvider(Context context,
IContentProvider provider, ProviderInfo info, boolean noisy) {
ContentProvider localProvider = null;
diff --git a/core/java/android/app/ProgressDialog.java b/core/java/android/app/ProgressDialog.java
index d421173..f1a04f8 100644
--- a/core/java/android/app/ProgressDialog.java
+++ b/core/java/android/app/ProgressDialog.java
@@ -343,7 +343,7 @@
private void onProgressChanged() {
if (mProgressStyle == STYLE_HORIZONTAL) {
- if (!mViewUpdateHandler.hasMessages(0)) {
+ if (mViewUpdateHandler != null && !mViewUpdateHandler.hasMessages(0)) {
mViewUpdateHandler.sendEmptyMessage(0);
}
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 28bc424..2236928 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -774,6 +774,32 @@
}
/**
+ * Get the current connection state of a profile.
+ * This function can be used to check whether the local Bluetooth adapter
+ * is connected to any remote device for a specific profile.
+ * Profile can be one of {@link BluetoothProfile#HEADSET},
+ * {@link BluetoothProfile#A2DP}.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
+ *
+ * <p> Return value can be one of
+ * {@link BluetoothProfile#STATE_DISCONNECTED},
+ * {@link BluetoothProfile#STATE_CONNECTING},
+ * {@link BluetoothProfile#STATE_CONNECTED},
+ * {@link BluetoothProfile#STATE_DISCONNECTING}
+ */
+ public int getProfileConnectionState(int profile) {
+ if (getState() != STATE_ON) return BluetoothProfile.STATE_DISCONNECTED;
+ try {
+ return mService.getProfileConnectionState(profile);
+ } catch (RemoteException e) {
+ Log.e(TAG, "getProfileConnectionState:", e);
+ }
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+
+ /**
+ /**
* Picks RFCOMM channels until none are left.
* Avoids reserved channels.
*/
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 6cd81fd..58b3868 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -83,6 +83,12 @@
public static final int PAN = 5;
/**
+ * PBAP
+ * @hide
+ */
+ public static final int PBAP = 6;
+
+ /**
* 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/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
index a7b0037..83d1bda 100644
--- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
+++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
@@ -19,7 +19,6 @@
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.DhcpInfoInternal;
-import android.net.LinkAddress;
import android.net.LinkCapabilities;
import android.net.LinkProperties;
import android.net.NetworkInfo;
@@ -30,7 +29,6 @@
import android.os.Message;
import android.util.Log;
-import java.net.InetAddress;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -184,11 +182,14 @@
return -1;
}
- /**
- * @param enabled
- */
- public void setDataEnable(boolean enabled) {
- android.util.Log.d(TAG, "setDataEnabled: IGNORING enabled=" + enabled);
+ @Override
+ public void setUserDataEnable(boolean enabled) {
+ Log.w(TAG, "ignoring setUserDataEnable(" + enabled + ")");
+ }
+
+ @Override
+ public void setPolicyDataEnable(boolean enabled) {
+ Log.w(TAG, "ignoring setPolicyDataEnable(" + enabled + ")");
}
/**
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 48dfed8..d4e7f7d 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -53,6 +53,7 @@
byte[] readOutOfBandData();
int getAdapterConnectionState();
+ int getProfileConnectionState(int profile);
boolean changeApplicationBluetoothState(boolean on,
in IBluetoothStateChangeCallback callback, in
IBinder b);
@@ -121,5 +122,5 @@
List<BluetoothDevice> getHealthDevicesMatchingConnectionStates(in int[] states);
int getHealthDeviceConnectionState(in BluetoothDevice device);
- void sendConnectionStateChange(in BluetoothDevice device, int state, int prevState);
+ void sendConnectionStateChange(in BluetoothDevice device, int profile, int state, int prevState);
}
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 08bb133..a41a330 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -1674,7 +1674,6 @@
* the focus mode to other modes.
*
* @see #FOCUS_MODE_CONTINUOUS_VIDEO
- * @hide
*/
public static final String FOCUS_MODE_CONTINUOUS_PICTURE = "continuous-picture";
diff --git a/core/java/android/net/DnsPinger.java b/core/java/android/net/DnsPinger.java
index 6115fef..3e27b0d 100644
--- a/core/java/android/net/DnsPinger.java
+++ b/core/java/android/net/DnsPinger.java
@@ -67,7 +67,7 @@
private final Context mContext;
private final int mConnectionType;
private final Handler mTarget;
- private final InetAddress mDefaultDns;
+ private final ArrayList<InetAddress> mDefaultDns;
private String TAG;
private static final int BASE = Protocol.BASE_DNS_PINGER;
@@ -113,7 +113,8 @@
throw new IllegalArgumentException("Invalid connectionType in constructor: "
+ connectionType);
}
- mDefaultDns = getDefaultDns();
+ mDefaultDns = new ArrayList<InetAddress>();
+ mDefaultDns.add(getDefaultDns());
mEventCounter = 0;
}
@@ -213,17 +214,16 @@
for (ActivePing activePing : mActivePings)
activePing.socket.close();
mActivePings.clear();
- removeMessages(ACTION_PING_DNS);
break;
}
}
/**
- * @return The first DNS in the link properties of the specified connection
- * type or the default system DNS if the link properties has null
- * dns set. Should not be null.
+ * Returns a list of DNS addresses, coming from either the link properties of the
+ * specified connection or the default system DNS if the link properties has no dnses.
+ * @return a non-empty non-null list
*/
- public InetAddress getDns() {
+ public List<InetAddress> getDnsList() {
LinkProperties curLinkProps = getCurrentLinkProperties();
if (curLinkProps == null) {
Slog.e(TAG, "getCurLinkProperties:: LP for type" + mConnectionType + " is null!");
@@ -236,7 +236,7 @@
return mDefaultDns;
}
- return dnses.iterator().next();
+ return new ArrayList<InetAddress>(dnses);
}
/**
diff --git a/core/java/android/net/DummyDataStateTracker.java b/core/java/android/net/DummyDataStateTracker.java
index e39725a..9f0f9cd 100644
--- a/core/java/android/net/DummyDataStateTracker.java
+++ b/core/java/android/net/DummyDataStateTracker.java
@@ -19,9 +19,6 @@
import android.content.Context;
import android.os.Handler;
import android.os.Message;
-import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkInfo;
-import android.net.LinkProperties;
import android.util.Slog;
/**
@@ -168,7 +165,14 @@
return true;
}
- public void setDataEnable(boolean enabled) {
+ @Override
+ public void setUserDataEnable(boolean enabled) {
+ // ignored
+ }
+
+ @Override
+ public void setPolicyDataEnable(boolean enabled) {
+ // ignored
}
@Override
diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java
index b035c51..21ecc22 100644
--- a/core/java/android/net/EthernetDataTracker.java
+++ b/core/java/android/net/EthernetDataTracker.java
@@ -17,15 +17,7 @@
package android.net;
import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.DhcpInfoInternal;
-import android.net.LinkAddress;
-import android.net.LinkCapabilities;
-import android.net.LinkProperties;
-import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkStateTracker;
-import android.net.NetworkUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.INetworkManagementService;
@@ -34,7 +26,6 @@
import android.os.ServiceManager;
import android.util.Log;
-import java.net.InetAddress;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -302,11 +293,14 @@
return -1;
}
- /**
- * @param enabled
- */
- public void setDataEnable(boolean enabled) {
- Log.d(TAG, "setDataEnabled: IGNORING enabled=" + enabled);
+ @Override
+ public void setUserDataEnable(boolean enabled) {
+ Log.w(TAG, "ignoring setUserDataEnable(" + enabled + ")");
+ }
+
+ @Override
+ public void setPolicyDataEnable(boolean enabled) {
+ Log.w(TAG, "ignoring setPolicyDataEnable(" + enabled + ")");
}
/**
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 1b95b60..c9553c0 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -64,9 +64,11 @@
boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress);
boolean getMobileDataEnabled();
-
void setMobileDataEnabled(boolean enabled);
+ /** Policy control over specific {@link NetworkStateTracker}. */
+ void setPolicyDataEnable(int networkType, boolean enabled);
+
int tether(String iface);
int untether(String iface);
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 5501f38..5929cfb 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -16,19 +16,26 @@
package android.net;
+import static com.android.internal.telephony.DataConnectionTracker.CMD_SET_POLICY_DATA_ENABLE;
+import static com.android.internal.telephony.DataConnectionTracker.CMD_SET_USER_DATA_ENABLE;
+import static com.android.internal.telephony.DataConnectionTracker.DISABLED;
+import static com.android.internal.telephony.DataConnectionTracker.ENABLED;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.net.NetworkInfo.DetailedState;
import android.os.Bundle;
-import android.os.HandlerThread;
+import android.os.Handler;
import android.os.Looper;
+import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
import android.os.ServiceManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Slog;
import com.android.internal.telephony.DataConnectionTracker;
import com.android.internal.telephony.ITelephony;
@@ -36,13 +43,6 @@
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.util.AsyncChannel;
-import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkInfo;
-import android.net.LinkProperties;
-import android.telephony.TelephonyManager;
-import android.util.Slog;
-import android.text.TextUtils;
-
/**
* Track the state of mobile data connectivity. This is done by
* receiving broadcast intents from the Phone process whenever
@@ -452,17 +452,22 @@
return false;
}
- /**
- * @param enabled
- */
- public void setDataEnable(boolean enabled) {
- try {
- if (DBG) log("setDataEnable: E enabled=" + enabled);
- mDataConnectionTrackerAc.sendMessage(DataConnectionTracker.CMD_SET_DATA_ENABLE,
- enabled ? DataConnectionTracker.ENABLED : DataConnectionTracker.DISABLED);
- if (VDBG) log("setDataEnable: X enabled=" + enabled);
- } catch (Exception e) {
- loge("setDataEnable: X mAc was null" + e);
+ @Override
+ public void setUserDataEnable(boolean enabled) {
+ if (DBG) log("setUserDataEnable: E enabled=" + enabled);
+ final AsyncChannel channel = mDataConnectionTrackerAc;
+ if (channel != null) {
+ channel.sendMessage(CMD_SET_USER_DATA_ENABLE, enabled ? ENABLED : DISABLED);
+ }
+ if (VDBG) log("setUserDataEnable: X enabled=" + enabled);
+ }
+
+ @Override
+ public void setPolicyDataEnable(boolean enabled) {
+ if (DBG) log("setPolicyDataEnable(enabled=" + enabled + ")");
+ final AsyncChannel channel = mDataConnectionTrackerAc;
+ if (channel != null) {
+ channel.sendMessage(CMD_SET_POLICY_DATA_ENABLE, enabled ? ENABLED : DISABLED);
}
}
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index f53063d..1735592 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -136,9 +136,17 @@
public boolean isAvailable();
/**
- * @param enabled
+ * User control of data connection through this network, typically persisted
+ * internally.
*/
- public void setDataEnable(boolean enabled);
+ public void setUserDataEnable(boolean enabled);
+
+ /**
+ * Policy control of data connection through this network, typically not
+ * persisted internally. Usually used when {@link NetworkPolicy#limitBytes}
+ * is passed.
+ */
+ public void setPolicyDataEnable(boolean enabled);
/**
* -------------------------------------------------------------
diff --git a/core/java/android/preference/SwitchPreference.java b/core/java/android/preference/SwitchPreference.java
index 17f0c1b..8bac6bd 100644
--- a/core/java/android/preference/SwitchPreference.java
+++ b/core/java/android/preference/SwitchPreference.java
@@ -41,14 +41,16 @@
private CharSequence mSwitchOff;
private final Listener mListener = new Listener();
- private class Listener implements View.OnClickListener, CompoundButton.OnCheckedChangeListener {
- @Override
- public void onClick(View v) {
- SwitchPreference.this.onClick();
- }
-
+ private class Listener implements CompoundButton.OnCheckedChangeListener {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (!callChangeListener(isChecked)) {
+ // Listener didn't like it, change it back.
+ // CompoundButton will make sure we don't recurse.
+ buttonView.setChecked(!isChecked);
+ return;
+ }
+
SwitchPreference.this.setChecked(isChecked);
}
}
@@ -111,12 +113,6 @@
switchView.setTextOff(mSwitchOff);
switchView.setOnCheckedChangeListener(mListener);
}
-
- if (checkableView.hasFocusable()) {
- // This is a focusable list item. Attach a click handler to toggle the button
- // for the rest of the item.
- view.setOnClickListener(mListener);
- }
}
syncSummaryView(view);
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index c5a924b..886edaf 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -202,6 +202,42 @@
public static final String GEOCODED_LOCATION = "geocoded_location";
/**
+ * The cached URI to look up the contact associated with the phone number, if it exists.
+ * This value is not guaranteed to be current, if the contact information
+ * associated with this number has changed.
+ * <P>Type: TEXT</P>
+ * @hide
+ */
+ public static final String CACHED_LOOKUP_URI = "lookup_uri";
+
+ /**
+ * The cached phone number of the contact which matches this entry, if it exists.
+ * This value is not guaranteed to be current, if the contact information
+ * associated with this number has changed.
+ * <P>Type: TEXT</P>
+ * @hide
+ */
+ public static final String CACHED_MATCHED_NUMBER = "matched_number";
+
+ /**
+ * The cached normalized version of the phone number, if it exists.
+ * This value is not guaranteed to be current, if the contact information
+ * associated with this number has changed.
+ * <P>Type: TEXT</P>
+ * @hide
+ */
+ public static final String CACHED_NORMALIZED_NUMBER = "normalized_number";
+
+ /**
+ * The cached photo id of the picture associated with the phone number, if it exists.
+ * This value is not guaranteed to be current, if the contact information
+ * associated with this number has changed.
+ * <P>Type: INTEGER (long)</P>
+ * @hide
+ */
+ public static final String CACHED_PHOTO_ID = "photo_id";
+
+ /**
* Adds a call to the call log.
*
* @param ci the CallerInfo object to get the target contact from. Can be null
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 554afd2..15c57e6 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2815,6 +2815,16 @@
public static final String TTS_DEFAULT_VARIANT = "tts_default_variant";
/**
+ * Stores the default tts locales on a per engine basis. Stored as
+ * a comma seperated list of values, each value being of the form
+ * {@code engine_name:locale} for example,
+ * {@code com.foo.ttsengine:eng-USA,com.bar.ttsengine:esp-ESP}.
+ *
+ * @hide
+ */
+ public static final String TTS_DEFAULT_LOCALE = "tts_default_locale";
+
+ /**
* Space delimited list of plugin packages that are enabled.
*/
public static final String TTS_ENABLED_PLUGINS = "tts_enabled_plugins";
@@ -3758,12 +3768,21 @@
/**
- * The {@link ComponentName} string of the service to be used as the spell checker
+ * The {@link ComponentName} string of the selected spell checker service which is
+ * one of the services managed by the text service manager.
+ *
+ * @hide
+ */
+ public static final String SELECTED_SPELL_CHECKER = "selected_spell_checker";
+
+ /**
+ * The {@link ComponentName} string of the selected subtype of the selected spell checker
* service which is one of the services managed by the text service manager.
*
* @hide
*/
- public static final String SPELL_CHECKER_SERVICE = "spell_checker_service";
+ public static final String SELECTED_SPELL_CHECKER_SUBTYPE =
+ "selected_spell_checker_subtype";
/**
* What happens when the user presses the Power button while in-call
diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java
index 8c04853..c4cb3a5 100644
--- a/core/java/android/server/BluetoothA2dpService.java
+++ b/core/java/android/server/BluetoothA2dpService.java
@@ -523,7 +523,8 @@
if (DBG) log("A2DP state : device: " + device + " State:" + prevState + "->" + state);
- mBluetoothService.sendConnectionStateChange(device, state, prevState);
+ mBluetoothService.sendConnectionStateChange(device, BluetoothProfile.A2DP, state,
+ prevState);
}
}
diff --git a/core/java/android/server/BluetoothHealthProfileHandler.java b/core/java/android/server/BluetoothHealthProfileHandler.java
index 105ff332..51c995e 100644
--- a/core/java/android/server/BluetoothHealthProfileHandler.java
+++ b/core/java/android/server/BluetoothHealthProfileHandler.java
@@ -20,6 +20,7 @@
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHealth;
import android.bluetooth.BluetoothHealthAppConfiguration;
+import android.bluetooth.BluetoothProfile;
import android.bluetooth.IBluetoothHealthCallback;
import android.content.Context;
import android.os.Handler;
@@ -567,7 +568,8 @@
private void updateAndSendIntent(BluetoothDevice device, int prevDeviceState,
int newDeviceState) {
mHealthDevices.put(device, newDeviceState);
- mBluetoothService.sendConnectionStateChange(device, prevDeviceState, newDeviceState);
+ mBluetoothService.sendConnectionStateChange(device, BluetoothProfile.HEALTH,
+ newDeviceState, prevDeviceState);
}
/**
diff --git a/core/java/android/server/BluetoothInputProfileHandler.java b/core/java/android/server/BluetoothInputProfileHandler.java
index 247e297..31764b0 100644
--- a/core/java/android/server/BluetoothInputProfileHandler.java
+++ b/core/java/android/server/BluetoothInputProfileHandler.java
@@ -20,6 +20,7 @@
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothDeviceProfileState;
import android.bluetooth.BluetoothInputDevice;
+import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProfileState;
import android.content.Context;
import android.content.Intent;
@@ -191,7 +192,8 @@
mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM);
debugLog("InputDevice state : device: " + device + " State:" + prevState + "->" + state);
- mBluetoothService.sendConnectionStateChange(device, state, prevState);
+ mBluetoothService.sendConnectionStateChange(device, BluetoothProfile.INPUT_DEVICE, state,
+ prevState);
}
void handleInputDevicePropertyChange(String address, boolean connected) {
diff --git a/core/java/android/server/BluetoothPanProfileHandler.java b/core/java/android/server/BluetoothPanProfileHandler.java
index 37cfdc4..bfad747 100644
--- a/core/java/android/server/BluetoothPanProfileHandler.java
+++ b/core/java/android/server/BluetoothPanProfileHandler.java
@@ -19,6 +19,7 @@
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothPan;
+import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothTetheringDataTracker;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -303,7 +304,8 @@
mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM);
debugLog("Pan Device state : device: " + device + " State:" + prevState + "->" + state);
- mBluetoothService.sendConnectionStateChange(device, state, prevState);
+ mBluetoothService.sendConnectionStateChange(device, BluetoothProfile.PAN, state,
+ prevState);
}
private class BluetoothPanDevice {
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index ee14673..e942969 100755
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -166,6 +166,7 @@
private static final String INCOMING_CONNECTION_FILE =
"/data/misc/bluetooth/incoming_connection.conf";
private HashMap<String, Pair<Integer, String>> mIncomingConnections;
+ private HashMap<Integer, Pair<Integer, Integer>> mProfileConnectionState;
private static class RemoteService {
public String address;
@@ -237,6 +238,7 @@
mBluetoothPanProfileHandler = BluetoothPanProfileHandler.getInstance(mContext, this);
mBluetoothHealthProfileHandler = BluetoothHealthProfileHandler.getInstance(mContext, this);
mIncomingConnections = new HashMap<String, Pair<Integer, String>>();
+ mProfileConnectionState = new HashMap<Integer, Pair<Integer, Integer>>();
}
public static synchronized String readDockBluetoothAddress() {
@@ -600,6 +602,11 @@
* It inits bond state and profile state before STATE_ON intent is broadcasted.
*/
/*package*/ void initBluetoothAfterTurningOn() {
+ String discoverable = getProperty("Discoverable", false);
+ String timeout = getProperty("DiscoverableTimeout", false);
+ if (discoverable.equals("true") && Integer.valueOf(timeout) != 0) {
+ setAdapterPropertyBooleanNative("Discoverable", 0);
+ }
mBondState.initBondState();
initProfileState();
}
@@ -1742,6 +1749,19 @@
dumpInputDeviceProfile(pw);
dumpPanProfile(pw);
dumpApplicationServiceRecords(pw);
+ dumpProfileState(pw);
+ }
+
+ private void dumpProfileState(PrintWriter pw) {
+ pw.println("\n--Profile State dump--");
+ pw.println("\n Headset profile state:" +
+ mAdapter.getProfileConnectionState(BluetoothProfile.HEADSET));
+ pw.println("\n A2dp profile state:" +
+ mAdapter.getProfileConnectionState(BluetoothProfile.A2DP));
+ pw.println("\n HID profile state:" +
+ mAdapter.getProfileConnectionState(BluetoothProfile.INPUT_DEVICE));
+ pw.println("\n PAN profile state:" +
+ mAdapter.getProfileConnectionState(BluetoothProfile.PAN));
}
private void dumpHeadsetService(PrintWriter pw) {
@@ -2443,23 +2463,85 @@
return mAdapterConnectionState;
}
- public synchronized void sendConnectionStateChange(BluetoothDevice device, int state,
- int prevState) {
+ public int getProfileConnectionState(int profile) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+
+ Pair<Integer, Integer> state = mProfileConnectionState.get(profile);
+ if (state == null) return BluetoothProfile.STATE_DISCONNECTED;
+
+ return state.first;
+ }
+
+ private void updateProfileConnectionState(int profile, int newState, int oldState) {
+ // mProfileConnectionState is a hashmap -
+ // <Integer, Pair<Integer, Integer>>
+ // The key is the profile, the value is a pair. first element
+ // is the state and the second element is the number of devices
+ // in that state.
+ int numDev = 1;
+ int newHashState = newState;
+ boolean update = true;
+
+ // The following conditions are considered in this function:
+ // 1. If there is no record of profile and state - update
+ // 2. If a new device's state is current hash state - increment
+ // number of devices in the state.
+ // 3. If a state change has happened to Connected or Connecting
+ // (if current state is not connected), update.
+ // 4. If numDevices is 1 and that device state is being updated, update
+ // 5. If numDevices is > 1 and one of the devices is changing state,
+ // decrement numDevices but maintain oldState if it is Connected or
+ // Connecting
+ Pair<Integer, Integer> stateNumDev = mProfileConnectionState.get(profile);
+ if (stateNumDev != null) {
+ int currHashState = stateNumDev.first;
+ numDev = stateNumDev.second;
+
+ if (newState == currHashState) {
+ numDev ++;
+ } else if (newState == BluetoothProfile.STATE_CONNECTED ||
+ (newState == BluetoothProfile.STATE_CONNECTING &&
+ currHashState != BluetoothProfile.STATE_CONNECTED)) {
+ numDev = 1;
+ } else if (numDev == 1 && oldState == currHashState) {
+ update = true;
+ } else if (numDev > 1 && oldState == currHashState) {
+ numDev --;
+
+ if (currHashState == BluetoothProfile.STATE_CONNECTED ||
+ currHashState == BluetoothProfile.STATE_CONNECTING) {
+ newHashState = currHashState;
+ }
+ } else {
+ update = false;
+ }
+ }
+
+ if (update) {
+ mProfileConnectionState.put(profile, new Pair<Integer, Integer>(newHashState,
+ numDev));
+ }
+ }
+
+ public synchronized void sendConnectionStateChange(BluetoothDevice
+ device, int profile, int state, int prevState) {
// Since this is a binder call check if Bluetooth is on still
if (getBluetoothStateInternal() == BluetoothAdapter.STATE_OFF) return;
- if (updateCountersAndCheckForConnectionStateChange(state, prevState)) {
- if (!validateProfileConnectionState(state) ||
- !validateProfileConnectionState(prevState)) {
- // Previously, an invalid state was broadcast anyway,
- // with the invalid state converted to -1 in the intent.
- // Better to log an error and not send an intent with
- // invalid contents or set mAdapterConnectionState to -1.
- Log.e(TAG, "Error in sendConnectionStateChange: "
- + "prevState " + prevState + " state " + state);
- return;
- }
+ if (!validateProfileConnectionState(state) ||
+ !validateProfileConnectionState(prevState)) {
+ // Previously, an invalid state was broadcast anyway,
+ // with the invalid state converted to -1 in the intent.
+ // Better to log an error and not send an intent with
+ // invalid contents or set mAdapterConnectionState to -1.
+ Log.e(TAG, "Error in sendConnectionStateChange: "
+ + "prevState " + prevState + " state " + state);
+ return;
+ }
+ updateProfileConnectionState(profile, state, prevState);
+
+ if (updateCountersAndCheckForConnectionStateChange(state, prevState)) {
mAdapterConnectionState = state;
if (state == BluetoothProfile.STATE_DISCONNECTED) {
diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java
index 79ade26..d78bbbf 100644
--- a/core/java/android/server/search/SearchManagerService.java
+++ b/core/java/android/server/search/SearchManagerService.java
@@ -97,8 +97,18 @@
* Refreshes the "searchables" list when packages are added/removed.
*/
class MyPackageMonitor extends PackageMonitor {
+
@Override
public void onSomePackagesChanged() {
+ updateSearchables();
+ }
+
+ @Override
+ public void onPackageModified(String pkg) {
+ updateSearchables();
+ }
+
+ private void updateSearchables() {
// Update list of searchable activities
getSearchables().buildSearchableList();
// Inform all listeners that the list of searchables has been updated.
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index c51ba2a..4c563ce 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -46,7 +46,6 @@
import android.view.InputDevice;
import android.view.InputHandler;
import android.view.InputQueue;
-import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.View;
@@ -54,8 +53,9 @@
import android.view.ViewRootImpl;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
-import android.view.WindowManagerPolicy;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
/**
@@ -459,6 +459,44 @@
public void onSurfaceDestroyed(SurfaceHolder holder) {
}
+ protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
+ out.print(prefix); out.print("mInitializing="); out.print(mInitializing);
+ out.print(" mDestroyed="); out.println(mDestroyed);
+ out.print(prefix); out.print("mVisible="); out.print(mVisible);
+ out.print(" mScreenOn="); out.print(mScreenOn);
+ out.print(" mReportedVisible="); out.println(mReportedVisible);
+ out.print(prefix); out.print("mCreated="); out.print(mCreated);
+ out.print(" mSurfaceCreated="); out.print(mSurfaceCreated);
+ out.print(" mIsCreating="); out.print(mIsCreating);
+ out.print(" mDrawingAllowed="); out.println(mDrawingAllowed);
+ out.print(prefix); out.print("mWidth="); out.print(mWidth);
+ out.print(" mCurWidth="); out.print(mCurWidth);
+ out.print(" mHeight="); out.print(mHeight);
+ out.print(" mCurHeight="); out.println(mCurHeight);
+ out.print(prefix); out.print("mType="); out.print(mType);
+ out.print(" mWindowFlags="); out.print(mWindowFlags);
+ out.print(" mCurWindowFlags="); out.println(mCurWindowFlags);
+ out.print(prefix); out.print("mVisibleInsets=");
+ out.print(mVisibleInsets.toShortString());
+ out.print(" mWinFrame="); out.print(mWinFrame.toShortString());
+ out.print(" mContentInsets="); out.println(mContentInsets.toShortString());
+ out.print(prefix); out.print("mConfiguration="); out.println(mConfiguration);
+ out.print(prefix); out.print("mLayout="); out.println(mLayout);
+ synchronized (mLock) {
+ out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset);
+ out.print(" mPendingXOffset="); out.println(mPendingXOffset);
+ out.print(prefix); out.print("mPendingXOffsetStep=");
+ out.print(mPendingXOffsetStep);
+ out.print(" mPendingXOffsetStep="); out.println(mPendingXOffsetStep);
+ out.print(prefix); out.print("mOffsetMessageEnqueued=");
+ out.print(mOffsetMessageEnqueued);
+ out.print(" mPendingSync="); out.println(mPendingSync);
+ if (mPendingMove != null) {
+ out.print(prefix); out.print("mPendingMove="); out.println(mPendingMove);
+ }
+ }
+ }
+
private void dispatchPointer(MotionEvent event) {
if (event.isTouchEvent()) {
synchronized (mLock) {
@@ -1012,4 +1050,14 @@
* is in the wallpaper picker viewing a preview of it as well.
*/
public abstract Engine onCreateEngine();
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter out, String[] args) {
+ out.print("State of wallpaper "); out.print(this); out.println(":");
+ for (int i=0; i<mActiveEngines.size(); i++) {
+ Engine engine = mActiveEngines.get(i);
+ out.print(" Engine "); out.print(engine); out.println(":");
+ engine.dump(" ", fd, out, args);
+ }
+ }
}
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index b4e8ab4..a08ba2a 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -65,6 +65,7 @@
*
* {@link #onStop} tells the engine that it should stop all ongoing synthesis, if
* any. Any pending data from the current synthesis will be discarded.
+ *
*/
// TODO: Add a link to the sample TTS engine once it's done.
public abstract class TextToSpeechService extends Service {
@@ -80,6 +81,7 @@
// associated with this TTS engine. Will handle all requests except synthesis
// to file requests, which occur on the synthesis thread.
private AudioPlaybackHandler mAudioPlaybackHandler;
+ private TtsEngines mEngineHelper;
private CallbackMap mCallbacks;
private String mPackageName;
@@ -96,12 +98,15 @@
mAudioPlaybackHandler = new AudioPlaybackHandler();
mAudioPlaybackHandler.start();
+ mEngineHelper = new TtsEngines(this);
+
mCallbacks = new CallbackMap();
mPackageName = getApplicationInfo().packageName;
+ String[] defaultLocale = getSettingsLocale();
// Load default language
- onLoadLanguage(getDefaultLanguage(), getDefaultCountry(), getDefaultVariant());
+ onLoadLanguage(defaultLocale[0], defaultLocale[1], defaultLocale[2]);
}
@Override
@@ -195,30 +200,15 @@
return getSecureSettingInt(Settings.Secure.TTS_DEFAULT_RATE, Engine.DEFAULT_RATE);
}
- private String getDefaultLanguage() {
- return getSecureSettingString(Settings.Secure.TTS_DEFAULT_LANG,
- Locale.getDefault().getISO3Language());
- }
-
- private String getDefaultCountry() {
- return getSecureSettingString(Settings.Secure.TTS_DEFAULT_COUNTRY,
- Locale.getDefault().getISO3Country());
- }
-
- private String getDefaultVariant() {
- return getSecureSettingString(Settings.Secure.TTS_DEFAULT_VARIANT,
- Locale.getDefault().getVariant());
+ private String[] getSettingsLocale() {
+ final String locale = mEngineHelper.getLocalePrefForEngine(mPackageName);
+ return TtsEngines.parseLocalePref(locale);
}
private int getSecureSettingInt(String name, int defaultValue) {
return Settings.Secure.getInt(getContentResolver(), name, defaultValue);
}
- private String getSecureSettingString(String name, String defaultValue) {
- String value = Settings.Secure.getString(getContentResolver(), name);
- return value != null ? value : defaultValue;
- }
-
/**
* Synthesizer thread. This thread is used to run {@link SynthHandler}.
*/
@@ -458,6 +448,7 @@
class SynthesisSpeechItem extends SpeechItem {
private final String mText;
private final SynthesisRequest mSynthesisRequest;
+ private final String[] mDefaultLocale;
// Non null after synthesis has started, and all accesses
// guarded by 'this'.
private AbstractSynthesisCallback mSynthesisCallback;
@@ -467,6 +458,7 @@
super(callingApp, params);
mText = text;
mSynthesisRequest = new SynthesisRequest(mText, mParams);
+ mDefaultLocale = getSettingsLocale();
setRequestParams(mSynthesisRequest);
mEventLogger = new EventLogger(mSynthesisRequest, getCallingApp(), mPackageName);
}
@@ -523,7 +515,7 @@
}
public String getLanguage() {
- return getStringParam(Engine.KEY_PARAM_LANGUAGE, getDefaultLanguage());
+ return getStringParam(Engine.KEY_PARAM_LANGUAGE, mDefaultLocale[0]);
}
private boolean hasLanguage() {
@@ -531,12 +523,12 @@
}
private String getCountry() {
- if (!hasLanguage()) return getDefaultCountry();
+ if (!hasLanguage()) return mDefaultLocale[1];
return getStringParam(Engine.KEY_PARAM_COUNTRY, "");
}
private String getVariant() {
- if (!hasLanguage()) return getDefaultVariant();
+ if (!hasLanguage()) return mDefaultLocale[2];
return getStringParam(Engine.KEY_PARAM_VARIANT, "");
}
diff --git a/core/java/android/speech/tts/TtsEngines.java b/core/java/android/speech/tts/TtsEngines.java
index 5f0cb74..bb72bea 100644
--- a/core/java/android/speech/tts/TtsEngines.java
+++ b/core/java/android/speech/tts/TtsEngines.java
@@ -17,6 +17,7 @@
import org.xmlpull.v1.XmlPullParserException;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -27,6 +28,8 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import static android.provider.Settings.Secure.getString;
+
import android.provider.Settings;
import android.speech.tts.TextToSpeech.Engine;
import android.speech.tts.TextToSpeech.EngineInfo;
@@ -40,6 +43,7 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.Locale;
/**
* Support class for querying the list of available engines
@@ -52,6 +56,9 @@
*/
public class TtsEngines {
private static final String TAG = "TtsEngines";
+ private static final boolean DBG = false;
+
+ private static final String LOCALE_DELIMITER = "-";
private final Context mContext;
@@ -65,7 +72,7 @@
* the highest ranked engine is returned as per {@link EngineInfoComparator}.
*/
public String getDefaultEngine() {
- String engine = Settings.Secure.getString(mContext.getContentResolver(),
+ String engine = getString(mContext.getContentResolver(),
Settings.Secure.TTS_DEFAULT_SYNTH);
return isEngineInstalled(engine) ? engine : getHighestRankedEngineName();
}
@@ -129,12 +136,6 @@
return engines;
}
- // TODO: Used only by the settings app. Remove once
- // the settings UI change has been finalized.
- public boolean isEngineEnabled(String engine) {
- return isEngineInstalled(engine);
- }
-
private boolean isSystemEngine(ServiceInfo info) {
final ApplicationInfo appInfo = info.applicationInfo;
return appInfo != null && (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
@@ -182,7 +183,7 @@
* The name of the XML tag that text to speech engines must use to
* declare their meta data.
*
- * {@link com.android.internal.R.styleable.TextToSpeechEngine}
+ * {@link com.android.internal.R.styleable#TextToSpeechEngine}
*/
private static final String XML_TAG_NAME = "tts-engine";
@@ -279,4 +280,175 @@
}
}
+ /**
+ * Returns the locale string for a given TTS engine. Attempts to read the
+ * value from {@link Settings.Secure#TTS_DEFAULT_LOCALE}, failing which the
+ * old style value from {@link Settings.Secure#TTS_DEFAULT_LANG} is read. If
+ * both these values are empty, the default phone locale is returned.
+ *
+ * @param engineName the engine to return the locale for.
+ * @return the locale string preference for this engine. Will be non null
+ * and non empty.
+ */
+ public String getLocalePrefForEngine(String engineName) {
+ String locale = parseEnginePrefFromList(
+ getString(mContext.getContentResolver(), Settings.Secure.TTS_DEFAULT_LOCALE),
+ engineName);
+
+ if (TextUtils.isEmpty(locale)) {
+ // The new style setting is unset, attempt to return the old style setting.
+ locale = getV1Locale();
+ }
+
+ if (DBG) Log.d(TAG, "getLocalePrefForEngine(" + engineName + ")= " + locale);
+
+ return locale;
+ }
+
+ /**
+ * Parses a locale preference value delimited by {@link #LOCALE_DELIMITER}.
+ * Varies from {@link String#split} in that it will always return an array
+ * of length 3 with non null values.
+ */
+ public static String[] parseLocalePref(String pref) {
+ String[] returnVal = new String[] { "", "", ""};
+ if (!TextUtils.isEmpty(pref)) {
+ String[] split = pref.split(LOCALE_DELIMITER);
+ System.arraycopy(split, 0, returnVal, 0, split.length);
+ }
+
+ if (DBG) Log.d(TAG, "parseLocalePref(" + returnVal[0] + "," + returnVal[1] +
+ "," + returnVal[2] +")");
+
+ return returnVal;
+ }
+
+ /**
+ * @return the old style locale string constructed from
+ * {@link Settings.Secure#TTS_DEFAULT_LANG},
+ * {@link Settings.Secure#TTS_DEFAULT_COUNTRY} and
+ * {@link Settings.Secure#TTS_DEFAULT_VARIANT}. If no such locale is set,
+ * then return the default phone locale.
+ */
+ private String getV1Locale() {
+ final ContentResolver cr = mContext.getContentResolver();
+
+ final String lang = Settings.Secure.getString(cr, Settings.Secure.TTS_DEFAULT_LANG);
+ final String country = Settings.Secure.getString(cr, Settings.Secure.TTS_DEFAULT_COUNTRY);
+ final String variant = Settings.Secure.getString(cr, Settings.Secure.TTS_DEFAULT_VARIANT);
+
+ if (TextUtils.isEmpty(lang)) {
+ return getDefaultLocale();
+ }
+
+ String v1Locale = lang;
+ if (!TextUtils.isEmpty(country)) {
+ v1Locale += LOCALE_DELIMITER + country;
+ }
+ if (!TextUtils.isEmpty(variant)) {
+ v1Locale += LOCALE_DELIMITER + variant;
+ }
+
+ return v1Locale;
+ }
+
+ private String getDefaultLocale() {
+ final Locale locale = Locale.getDefault();
+
+ return locale.getISO3Language() + LOCALE_DELIMITER + locale.getISO3Country() +
+ LOCALE_DELIMITER + locale.getVariant();
+ }
+
+ /**
+ * Parses a comma separated list of engine locale preferences. The list is of the
+ * form {@code "engine_name_1:locale_1,engine_name_2:locale2"} and so on and
+ * so forth. Returns null if the list is empty, malformed or if there is no engine
+ * specific preference in the list.
+ */
+ private static String parseEnginePrefFromList(String prefValue, String engineName) {
+ if (TextUtils.isEmpty(prefValue)) {
+ return null;
+ }
+
+ String[] prefValues = prefValue.split(",");
+
+ for (String value : prefValues) {
+ final int delimiter = value.indexOf(':');
+ if (delimiter > 0) {
+ if (engineName.equals(value.substring(0, delimiter))) {
+ return value.substring(delimiter + 1);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public synchronized void updateLocalePrefForEngine(String name, String newLocale) {
+ final String prefList = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.TTS_DEFAULT_LOCALE);
+ if (DBG) {
+ Log.d(TAG, "updateLocalePrefForEngine(" + name + ", " + newLocale +
+ "), originally: " + prefList);
+ }
+
+ final String newPrefList = updateValueInCommaSeparatedList(prefList,
+ name, newLocale);
+
+ if (DBG) Log.d(TAG, "updateLocalePrefForEngine(), writing: " + newPrefList.toString());
+
+ Settings.Secure.putString(mContext.getContentResolver(),
+ Settings.Secure.TTS_DEFAULT_LOCALE, newPrefList.toString());
+ }
+
+ /**
+ * Updates the value for a given key in a comma separated list of key value pairs,
+ * each of which are delimited by a colon. If no value exists for the given key,
+ * the kay value pair are appended to the end of the list.
+ */
+ private String updateValueInCommaSeparatedList(String list, String key,
+ String newValue) {
+ StringBuilder newPrefList = new StringBuilder();
+ if (TextUtils.isEmpty(list)) {
+ // If empty, create a new list with a single entry.
+ newPrefList.append(key).append(':').append(newValue);
+ } else {
+ String[] prefValues = list.split(",");
+ // Whether this is the first iteration in the loop.
+ boolean first = true;
+ // Whether we found the given key.
+ boolean found = false;
+ for (String value : prefValues) {
+ final int delimiter = value.indexOf(':');
+ if (delimiter > 0) {
+ if (key.equals(value.substring(0, delimiter))) {
+ if (first) {
+ first = false;
+ } else {
+ newPrefList.append(',');
+ }
+ found = true;
+ newPrefList.append(key).append(':').append(newValue);
+ } else {
+ if (first) {
+ first = false;
+ } else {
+ newPrefList.append(',');
+ }
+ // Copy across the entire key + value as is.
+ newPrefList.append(value);
+ }
+ }
+ }
+
+ if (!found) {
+ // Not found, but the rest of the keys would have been copied
+ // over already, so just append it to the end.
+ newPrefList.append(',');
+ newPrefList.append(key).append(':').append(newValue);
+ }
+ }
+
+ return newPrefList.toString();
+ }
}
diff --git a/core/java/android/text/TextDirectionHeuristics.java b/core/java/android/text/TextDirectionHeuristics.java
index 5ed2df4..6debc6b 100644
--- a/core/java/android/text/TextDirectionHeuristics.java
+++ b/core/java/android/text/TextDirectionHeuristics.java
@@ -50,22 +50,6 @@
new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, true);
/**
- * If the text contains any strong left to right non-format character, determines
- * that the direction is left to right, falling back to left to right if it
- * finds none.
- */
- public static final TextDirectionHeuristic ANYLTR_LTR =
- new TextDirectionHeuristicInternal(AnyStrong.INSTANCE_LTR, false);
-
- /**
- * If the text contains any strong left to right non-format character, determines
- * that the direction is left to right, falling back to right to left if it
- * finds none.
- */
- public static final TextDirectionHeuristic ANYLTR_RTL =
- new TextDirectionHeuristicInternal(AnyStrong.INSTANCE_LTR, true);
-
- /**
* If the text contains any strong right to left non-format character, determines
* that the direction is right to left, falling back to left to right if it
* finds none.
@@ -74,14 +58,6 @@
new TextDirectionHeuristicInternal(AnyStrong.INSTANCE_RTL, false);
/**
- * If the text contains any strong right to left non-format character, determines
- * that the direction is right to left, falling back to right to left if it
- * finds none.
- */
- public static final TextDirectionHeuristic ANYRTL_RTL =
- new TextDirectionHeuristicInternal(AnyStrong.INSTANCE_RTL, true);
-
- /**
* Examines only the strong directional non-format characters, and if either
* left to right or right to left characters are 60% or more of this total,
* determines that the direction follows the majority of characters. Falls
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c7bf8e3..0dc781f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -218,4 +218,9 @@
* Called by the settings application to temporarily set the pointer speed.
*/
void setPointerSpeed(int speed);
+
+ /**
+ * Block until all windows the window manager knows about have been drawn.
+ */
+ void waitForAllDrawn();
}
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 7a96a50..5e7e509 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -163,7 +163,9 @@
* It is an error to lock a Blur surface, since it doesn't have
* a backing store.
* @hide
+ * @deprecated
*/
+ @Deprecated
public static final int FX_SURFACE_BLUR = 0x00010000;
/** Creates a Dim surface. Everything behind this surface is dimmed
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index abd9ad6..17e637c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8087,6 +8087,11 @@
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);
}
+ if ((mViewFlags & VISIBILITY_MASK) != VISIBLE && mCurrentAnimation == null) {
+ // Noop for views which are not visible and which are not running an animation. They
+ // will not get drawn and they should not set dirty flags as if they will be drawn
+ return;
+ }
if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) ||
(mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID ||
(mPrivateFlags & INVALIDATED) != INVALIDATED) {
@@ -8130,6 +8135,11 @@
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);
}
+ if ((mViewFlags & VISIBILITY_MASK) != VISIBLE && mCurrentAnimation == null) {
+ // Noop for views which are not visible and which are not running an animation. They
+ // will not get drawn and they should not set dirty flags as if they will be drawn
+ return;
+ }
if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) ||
(mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID ||
(mPrivateFlags & INVALIDATED) != INVALIDATED) {
@@ -8182,6 +8192,11 @@
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);
}
+ if ((mViewFlags & VISIBILITY_MASK) != VISIBLE && mCurrentAnimation == null) {
+ // Noop for views which are not visible and which are not running an animation. They
+ // will not get drawn and they should not set dirty flags as if they will be drawn
+ return;
+ }
if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) ||
(invalidateCache && (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) ||
(mPrivateFlags & INVALIDATED) != INVALIDATED || isOpaque() != mLastIsOpaque) {
@@ -8217,6 +8232,11 @@
* @hide
*/
public void fastInvalidate() {
+ if ((mViewFlags & VISIBILITY_MASK) != VISIBLE && mCurrentAnimation == null) {
+ // Noop for views which are not visible and which are not running an animation. They
+ // will not get drawn and they should not set dirty flags as if they will be drawn
+ return;
+ }
if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) ||
(mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID ||
(mPrivateFlags & INVALIDATED) != INVALIDATED) {
@@ -9165,7 +9185,8 @@
mPrivateFlags &= ~AWAKEN_SCROLL_BARS_ON_ATTACH;
}
jumpDrawablesToCurrentState();
- // Order is important here: LayoutDirection should be resolved before Padding and TextDirection
+ // Order is important here: LayoutDirection MUST be resolved before Padding
+ // and TextDirection
resolveLayoutDirectionIfNeeded();
resolvePadding();
resolveTextDirection();
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 8fa1922..7c826ca 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2628,7 +2628,8 @@
final int left = cl + (int) region.left;
final int top = ct + (int) region.top;
- invalidate(left, top, left + (int) region.width(), top + (int) region.height());
+ invalidate(left, top, left + (int) (region.width() + .5f),
+ top + (int) (region.height() + .5f));
}
}
} else if ((flags & FLAG_SUPPORT_STATIC_TRANSFORMATIONS) ==
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
index e1aa9a4..fb87e23 100644
--- a/core/java/android/view/VolumePanel.java
+++ b/core/java/android/view/VolumePanel.java
@@ -259,8 +259,9 @@
mStreamControls = new HashMap<Integer,StreamControl>(STREAM_TYPES.length);
Resources res = mContext.getResources();
for (int i = 0; i < STREAM_TYPES.length; i++) {
+ final int streamType = STREAM_TYPES[i];
StreamControl sc = new StreamControl();
- sc.streamType = STREAM_TYPES[i];
+ sc.streamType = streamType;
sc.group = (ViewGroup) inflater.inflate(R.layout.volume_adjust_item, null);
sc.group.setTag(sc);
sc.icon = (ImageView) sc.group.findViewById(R.id.stream_icon);
@@ -273,10 +274,12 @@
sc.iconMuteRes = STREAM_ICONS_MUTED[i];
sc.icon.setImageResource(sc.iconRes);
sc.seekbarView = (SeekBar) sc.group.findViewById(R.id.seekbar);
- sc.seekbarView.setMax(mAudioManager.getStreamMaxVolume(STREAM_TYPES[i]));
+ int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
+ streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
+ sc.seekbarView.setMax(mAudioManager.getStreamMaxVolume(streamType) + plusOne);
sc.seekbarView.setOnSeekBarChangeListener(this);
sc.seekbarView.setTag(sc);
- mStreamControls.put(STREAM_TYPES[i], sc);
+ mStreamControls.put(streamType, sc);
}
}
@@ -476,6 +479,9 @@
StreamControl sc = mStreamControls.get(streamType);
if (sc != null) {
+ if (sc.seekbarView.getMax() != max) {
+ sc.seekbarView.setMax(max);
+ }
sc.seekbarView.setProgress(index);
}
@@ -557,28 +563,6 @@
}
}
-// /**
-// * Makes the ringer icon visible with an icon that is chosen
-// * based on the current ringer mode.
-// */
-// private void setRingerIcon() {
-// mSmallStreamIcon.setVisibility(View.GONE);
-// mLargeStreamIcon.setVisibility(View.VISIBLE);
-//
-// int ringerMode = mAudioService.getRingerMode();
-// int icon;
-//
-// if (LOGD) Log.d(TAG, "setRingerIcon(), ringerMode: " + ringerMode);
-//
-// if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
-// icon = com.android.internal.R.drawable.ic_volume_off;
-// } else if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
-// icon = com.android.internal.R.drawable.ic_vibrate;
-// } else {
-// icon = com.android.internal.R.drawable.ic_volume;
-// }
-// mLargeStreamIcon.setImageResource(icon);
-// }
/**
* Switch between icons because Bluetooth music is same as music volume, but with
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 6b09049..fdd9b2c 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -177,7 +177,8 @@
@ViewDebug.IntToString(from = TYPE_SYSTEM_ERROR, to = "TYPE_SYSTEM_ERROR"),
@ViewDebug.IntToString(from = TYPE_INPUT_METHOD, to = "TYPE_INPUT_METHOD"),
@ViewDebug.IntToString(from = TYPE_INPUT_METHOD_DIALOG, to = "TYPE_INPUT_METHOD_DIALOG"),
- @ViewDebug.IntToString(from = TYPE_SECURE_SYSTEM_OVERLAY, to = "TYPE_SECURE_SYSTEM_OVERLAY")
+ @ViewDebug.IntToString(from = TYPE_SECURE_SYSTEM_OVERLAY, to = "TYPE_SECURE_SYSTEM_OVERLAY"),
+ @ViewDebug.IntToString(from = TYPE_BOOT_PROGRESS, to = "TYPE_BOOT_PROGRESS")
})
public int type;
@@ -401,6 +402,13 @@
public static final int TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20;
/**
+ * Window type: The boot progress dialog, goes on top of everything
+ * in the world.
+ * @hide
+ */
+ public static final int TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21;
+
+ /**
* End of types of system windows.
*/
public static final int LAST_SYSTEM_WINDOW = 2999;
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index b7dfabc..c1eec6f 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -116,7 +116,8 @@
/**
* Indicates whether the animation transformation should be applied before the
- * animation starts.
+ * animation starts. The value of this variable is only relevant if mFillEnabled is true;
+ * otherwise it is assumed to be true.
*/
boolean mFillBefore = true;
@@ -127,7 +128,7 @@
boolean mFillAfter = false;
/**
- * Indicates whether fillAfter should be taken into account.
+ * Indicates whether fillBefore should be taken into account.
*/
boolean mFillEnabled = false;
@@ -440,6 +441,7 @@
*/
public void scaleCurrentDuration(float scale) {
mDuration = (long) (mDuration * scale);
+ mStartOffset = (long) (mStartOffset * scale);
}
/**
@@ -505,9 +507,9 @@
}
/**
- * If fillEnabled is true, this animation will apply fillBefore and fillAfter.
+ * If fillEnabled is true, this animation will apply the value of fillBefore.
*
- * @return true if the animation will take fillBefore and fillAfter into account
+ * @return true if the animation will take fillBefore into account
* @attr ref android.R.styleable#Animation_fillEnabled
*/
public boolean isFillEnabled() {
@@ -515,11 +517,11 @@
}
/**
- * If fillEnabled is true, the animation will apply the value of fillBefore and
- * fillAfter. Otherwise, fillBefore and fillAfter are ignored and the animation
- * transformation is always applied.
+ * If fillEnabled is true, the animation will apply the value of fillBefore.
+ * Otherwise, fillBefore is ignored and the animation
+ * transformation is always applied until the animation ends.
*
- * @param fillEnabled true if the animation should take fillBefore and fillAfter into account
+ * @param fillEnabled true if the animation should take the value of fillBefore into account
* @attr ref android.R.styleable#Animation_fillEnabled
*
* @see #setFillBefore(boolean)
@@ -531,7 +533,8 @@
/**
* If fillBefore is true, this animation will apply its transformation
- * before the start time of the animation. Defaults to true if not set.
+ * before the start time of the animation. Defaults to true if
+ * {@link #setFillEnabled(boolean)} is not set to true.
* Note that this applies when using an {@link
* android.view.animation.AnimationSet AnimationSet} to chain
* animations. The transformation is not applied before the AnimationSet
@@ -549,10 +552,9 @@
/**
* If fillAfter is true, the transformation that this animation performed
* will persist when it is finished. Defaults to false if not set.
- * Note that this applies when using an {@link
+ * Note that this applies to individual animations and when using an {@link
* android.view.animation.AnimationSet AnimationSet} to chain
- * animations. The transformation is not applied before the AnimationSet
- * itself starts.
+ * animations.
*
* @param fillAfter true if the animation should apply its transformation after it ends
* @attr ref android.R.styleable#Animation_fillAfter
@@ -674,7 +676,9 @@
/**
* If fillBefore is true, this animation will apply its transformation
- * before the start time of the animation.
+ * before the start time of the animation. If fillBefore is false and
+ * {@link #isFillEnabled() fillEnabled} is true, the transformation will not be applied until
+ * the start time of the animation.
*
* @return true if the animation applies its transformation before it starts
* @attr ref android.R.styleable#Animation_fillBefore
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index d60ce4f..bb13052 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -135,11 +135,40 @@
public void setCurrentSpellChecker(SpellCheckerInfo sci) {
try {
if (sci == null) {
- throw new NullPointerException("SpellCheckerInfo is null");
+ throw new NullPointerException("SpellCheckerInfo is null.");
}
- sService.setCurrentSpellChecker(sci.getId());
+ sService.setCurrentSpellChecker(null, sci.getId());
} catch (RemoteException e) {
Log.e(TAG, "Error in setCurrentSpellChecker: " + e);
}
}
+
+ /**
+ * @hide
+ */
+ public SpellCheckerSubtype getCurrentSpellCheckerSubtype() {
+ try {
+ // Passing null as a locale for ICS
+ return sService.getCurrentSpellCheckerSubtype(null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in getCurrentSpellCheckerSubtype: " + e);
+ return null;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void setSpellCheckerSubtype(SpellCheckerSubtype subtype) {
+ try {
+ if (subtype == null) {
+ throw new NullPointerException("SpellCheckerSubtype is null.");
+ }
+ sService.setCurrentSpellCheckerSubtype(null, subtype.hashCode());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in setSpellCheckerSubtype:" + e);
+ }
+ }
+
+
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index dae118e..86f061a 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -54,6 +54,7 @@
import android.view.ViewConfiguration;
import android.view.ViewDebug;
import android.view.ViewGroup;
+import android.view.ViewParent;
import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -2788,7 +2789,10 @@
reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
// Time to start stealing events! Once we've stolen them, don't let anyone
// steal from us
- requestDisallowInterceptTouchEvent(true);
+ final ViewParent parent = getParent();
+ if (parent != null) {
+ parent.requestDisallowInterceptTouchEvent(true);
+ }
return true;
}
@@ -2848,9 +2852,7 @@
View v;
int deltaY;
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
+ initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(ev);
switch (action & MotionEvent.ACTION_MASK) {
@@ -2955,7 +2957,10 @@
// Make sure that we do so in case we're in a parent that can intercept.
if ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) == 0 &&
Math.abs(deltaY) > mTouchSlop) {
- requestDisallowInterceptTouchEvent(true);
+ final ViewParent parent = getParent();
+ if (parent != null) {
+ parent.requestDisallowInterceptTouchEvent(true);
+ }
}
final int rawDeltaY = deltaY;
@@ -2998,7 +3003,9 @@
0, mOverscrollDistance, true);
if (Math.abs(mOverscrollDistance) == Math.abs(mScrollY)) {
// Don't allow overfling if we're at the edge.
- mVelocityTracker.clear();
+ if (mVelocityTracker != null) {
+ mVelocityTracker.clear();
+ }
}
final int overscrollMode = getOverScrollMode();
@@ -3259,10 +3266,7 @@
handler.removeCallbacks(mPendingCheckForLongPress);
}
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
+ recycleVelocityTracker();
mActivePointerId = INVALID_POINTER;
@@ -3307,10 +3311,7 @@
handler.removeCallbacks(mPendingCheckForLongPress);
}
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
+ recycleVelocityTracker();
}
if (mEdgeGlowTop != null) {
@@ -3450,6 +3451,35 @@
mGlowPaddingRight = rightPadding;
}
+ private void initOrResetVelocityTracker() {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ } else {
+ mVelocityTracker.clear();
+ }
+ }
+
+ private void initVelocityTrackerIfNotExists() {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ }
+
+ private void recycleVelocityTracker() {
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+ }
+
+ @Override
+ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ if (disallowIntercept) {
+ recycleVelocityTracker();
+ }
+ super.requestDisallowInterceptTouchEvent(disallowIntercept);
+ }
+
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
@@ -3487,6 +3517,8 @@
clearScrollingCache();
}
mLastY = Integer.MIN_VALUE;
+ initOrResetVelocityTracker();
+ mVelocityTracker.addMovement(ev);
if (touchMode == TOUCH_MODE_FLING) {
return true;
}
@@ -3502,6 +3534,8 @@
mActivePointerId = ev.getPointerId(pointerIndex);
}
final int y = (int) ev.getY(pointerIndex);
+ initVelocityTrackerIfNotExists();
+ mVelocityTracker.addMovement(ev);
if (startScrollIfNeeded(y - mMotionY)) {
return true;
}
@@ -3510,9 +3544,11 @@
break;
}
+ case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
mTouchMode = TOUCH_MODE_REST;
mActivePointerId = INVALID_POINTER;
+ recycleVelocityTracker();
reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
break;
}
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index b428301..d638732 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -399,6 +399,35 @@
return false;
}
+ private void initOrResetVelocityTracker() {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ } else {
+ mVelocityTracker.clear();
+ }
+ }
+
+ private void initVelocityTrackerIfNotExists() {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ }
+
+ private void recycleVelocityTracker() {
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+ }
+
+ @Override
+ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ if (disallowIntercept) {
+ recycleVelocityTracker();
+ }
+ super.requestDisallowInterceptTouchEvent(disallowIntercept);
+ }
+
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
/*
@@ -440,6 +469,8 @@
if (xDiff > mTouchSlop) {
mIsBeingDragged = true;
mLastMotionX = x;
+ initVelocityTrackerIfNotExists();
+ mVelocityTracker.addMovement(ev);
if (mParent != null) mParent.requestDisallowInterceptTouchEvent(true);
}
break;
@@ -449,6 +480,7 @@
final float x = ev.getX();
if (!inChild((int) x, (int) ev.getY())) {
mIsBeingDragged = false;
+ recycleVelocityTracker();
break;
}
@@ -459,6 +491,9 @@
mLastMotionX = x;
mActivePointerId = ev.getPointerId(0);
+ initOrResetVelocityTracker();
+ mVelocityTracker.addMovement(ev);
+
/*
* If being flinged and user touches the screen, initiate drag;
* otherwise don't. mScroller.isFinished should be false when
@@ -498,9 +533,7 @@
@Override
public boolean onTouchEvent(MotionEvent ev) {
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
+ initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(ev);
final int action = ev.getAction();
@@ -584,11 +617,8 @@
mActivePointerId = INVALID_POINTER;
mIsBeingDragged = false;
+ recycleVelocityTracker();
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
if (mEdgeGlowLeft != null) {
mEdgeGlowLeft.onRelease();
mEdgeGlowRight.onRelease();
@@ -602,10 +632,8 @@
}
mActivePointerId = INVALID_POINTER;
mIsBeingDragged = false;
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
+ recycleVelocityTracker();
+
if (mEdgeGlowLeft != null) {
mEdgeGlowLeft.onRelease();
mEdgeGlowRight.onRelease();
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index e59f731..09c875b 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -408,6 +408,36 @@
return false;
}
+ private void initOrResetVelocityTracker() {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ } else {
+ mVelocityTracker.clear();
+ }
+ }
+
+ private void initVelocityTrackerIfNotExists() {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ }
+
+ private void recycleVelocityTracker() {
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+ }
+
+ @Override
+ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ if (disallowIntercept) {
+ recycleVelocityTracker();
+ }
+ super.requestDisallowInterceptTouchEvent(disallowIntercept);
+ }
+
+
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
/*
@@ -449,6 +479,8 @@
if (yDiff > mTouchSlop) {
mIsBeingDragged = true;
mLastMotionY = y;
+ initVelocityTrackerIfNotExists();
+ mVelocityTracker.addMovement(ev);
if (mScrollStrictSpan == null) {
mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");
}
@@ -460,6 +492,7 @@
final float y = ev.getY();
if (!inChild((int) ev.getX(), (int) y)) {
mIsBeingDragged = false;
+ recycleVelocityTracker();
break;
}
@@ -470,6 +503,8 @@
mLastMotionY = y;
mActivePointerId = ev.getPointerId(0);
+ initOrResetVelocityTracker();
+ mVelocityTracker.addMovement(ev);
/*
* If being flinged and user touches the screen, initiate drag;
* otherwise don't. mScroller.isFinished should be false when
@@ -487,6 +522,7 @@
/* Release the drag */
mIsBeingDragged = false;
mActivePointerId = INVALID_POINTER;
+ recycleVelocityTracker();
if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) {
invalidate();
}
@@ -505,9 +541,7 @@
@Override
public boolean onTouchEvent(MotionEvent ev) {
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
+ initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(ev);
final int action = ev.getAction();
@@ -1441,10 +1475,7 @@
private void endDrag() {
mIsBeingDragged = false;
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
+ recycleVelocityTracker();
if (mEdgeGlowTop != null) {
mEdgeGlowTop.onRelease();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 7a5a091..662b964 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10745,8 +10745,7 @@
TextDirectionHeuristics.FIRSTSTRONG_LTR);
break;
case TEXT_DIRECTION_ANY_RTL:
- mTextDir = (defaultIsRtl ? TextDirectionHeuristics.ANYRTL_RTL:
- TextDirectionHeuristics.ANYRTL_LTR);
+ mTextDir = TextDirectionHeuristics.ANYRTL_LTR;
break;
case TEXT_DIRECTION_CHAR_COUNT:
mTextDir = (defaultIsRtl ? TextDirectionHeuristics.CHARCOUNT_RTL:
diff --git a/core/java/com/android/internal/app/ShutdownThread.java b/core/java/com/android/internal/app/ShutdownThread.java
index d1c3e64..daabf42 100644
--- a/core/java/com/android/internal/app/ShutdownThread.java
+++ b/core/java/com/android/internal/app/ShutdownThread.java
@@ -122,10 +122,6 @@
closer.dialog = dialog;
dialog.setOnDismissListener(closer);
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- if (!context.getResources().getBoolean(
- com.android.internal.R.bool.config_sf_slowBlur)) {
- dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
- }
dialog.show();
} else {
beginShutdownSequence(context);
@@ -185,10 +181,6 @@
pd.setIndeterminate(true);
pd.setCancelable(false);
pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- if (!context.getResources().getBoolean(
- com.android.internal.R.bool.config_sf_slowBlur)) {
- pd.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
- }
pd.show();
diff --git a/core/java/com/android/internal/textservice/ITextServicesManager.aidl b/core/java/com/android/internal/textservice/ITextServicesManager.aidl
index bb4b2a3..cc30c176 100644
--- a/core/java/com/android/internal/textservice/ITextServicesManager.aidl
+++ b/core/java/com/android/internal/textservice/ITextServicesManager.aidl
@@ -22,6 +22,7 @@
import android.content.ComponentName;
import android.os.Bundle;
import android.view.textservice.SpellCheckerInfo;
+import android.view.textservice.SpellCheckerSubtype;
/**
* Interface to the text service manager.
@@ -29,10 +30,12 @@
*/
interface ITextServicesManager {
SpellCheckerInfo getCurrentSpellChecker(String locale);
+ SpellCheckerSubtype getCurrentSpellCheckerSubtype(String locale);
oneway void getSpellCheckerService(String sciId, in String locale,
in ITextServicesSessionListener tsListener,
in ISpellCheckerSessionListener scListener, in Bundle bundle);
oneway void finishSpellCheckerService(in ISpellCheckerSessionListener listener);
- oneway void setCurrentSpellChecker(String sciId);
+ oneway void setCurrentSpellChecker(String locale, String sciId);
+ oneway void setCurrentSpellCheckerSubtype(String locale, int hashCode);
SpellCheckerInfo[] getEnabledSpellCheckers();
}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
index df4243a..246c4de 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
@@ -252,7 +252,7 @@
* @return true if the overflow menu was shown, false otherwise.
*/
public boolean showOverflowMenu() {
- if (mReserveOverflow && !isOverflowMenuShowing() && mMenuView != null &&
+ if (mReserveOverflow && !isOverflowMenuShowing() && mMenu != null && mMenuView != null &&
mPostedOpenRunnable == null) {
OverflowPopup popup = new OverflowPopup(mContext, mMenu, mOverflowButton, true);
mPostedOpenRunnable = new OpenOverflowRunnable(popup);
diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java
index d613921..b355c41 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuView.java
@@ -31,6 +31,7 @@
private static final String TAG = "ActionMenuView";
static final int MIN_CELL_SIZE = 56; // dips
+ static final int GENERATED_ITEM_PADDING = 4; // dips
private MenuBuilder mMenu;
@@ -39,6 +40,7 @@
private boolean mFormatItems;
private int mFormatItemsWidth;
private int mMinCellSize;
+ private int mGeneratedItemPadding;
private int mMeasuredExtraWidth;
public ActionMenuView(Context context) {
@@ -48,7 +50,9 @@
public ActionMenuView(Context context, AttributeSet attrs) {
super(context, attrs);
setBaselineAligned(false);
- mMinCellSize = (int) (MIN_CELL_SIZE * context.getResources().getDisplayMetrics().density);
+ final float density = context.getResources().getDisplayMetrics().density;
+ mMinCellSize = (int) (MIN_CELL_SIZE * density);
+ mGeneratedItemPadding = (int) (GENERATED_ITEM_PADDING * density);
}
public void setPresenter(ActionMenuPresenter presenter) {
@@ -133,8 +137,15 @@
final View child = getChildAt(i);
if (child.getVisibility() == GONE) continue;
+ final boolean isGeneratedItem = child instanceof ActionMenuItemView;
visibleItemCount++;
+ if (isGeneratedItem) {
+ // Reset padding for generated menu item views; it may change below
+ // and views are recycled.
+ child.setPadding(mGeneratedItemPadding, 0, mGeneratedItemPadding, 0);
+ }
+
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
lp.expanded = false;
lp.extraPixels = 0;
@@ -142,6 +153,7 @@
lp.expandable = false;
lp.leftMargin = 0;
lp.rightMargin = 0;
+ lp.preventEdgeOffset = isGeneratedItem && ((ActionMenuItemView) child).hasText();
// Overflow always gets 1 cell. No more, no less.
final int cellsAvailable = lp.isOverflowButton ? 1 : cellsRemaining;
@@ -158,6 +170,10 @@
if (cellsUsed == 1) smallestItemsAt |= (1 << i);
}
+ // When we have overflow and a single expanded (text) item, we want to try centering it
+ // visually in the available space even though overflow consumes some of it.
+ final boolean centerSingleExpandedItem = hasOverflow && visibleItemCount == 2;
+
// Divide space for remaining cells if we have items that can expand.
// Try distributing whole leftover cells to smaller items first.
@@ -184,16 +200,27 @@
}
}
- if (minCellsItemCount > cellsRemaining) break; // Couldn't expand anything evenly. Stop.
-
// Items that get expanded will always be in the set of smallest items when we're done.
smallestItemsAt |= minCellsAt;
- for (int i = 0; i < childCount; i++) {
- if ((minCellsAt & (1 << i)) == 0) continue;
+ if (minCellsItemCount > cellsRemaining) break; // Couldn't expand anything evenly. Stop.
+ // We have enough cells, all minimum size items will be incremented.
+ minCells++;
+
+ for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if ((minCellsAt & (1 << i)) == 0) {
+ // If this item is already at our small item count, mark it for later.
+ if (lp.cellsUsed == minCells) smallestItemsAt |= 1 << i;
+ continue;
+ }
+
+ if (centerSingleExpandedItem && lp.preventEdgeOffset && cellsRemaining == 1) {
+ // Add padding to this item such that it centers.
+ child.setPadding(mGeneratedItemPadding + cellSize, 0, mGeneratedItemPadding, 0);
+ }
lp.cellsUsed++;
lp.expanded = true;
cellsRemaining--;
@@ -207,16 +234,18 @@
final boolean singleItem = !hasOverflow && visibleItemCount == 1;
if (cellsRemaining > 0 && smallestItemsAt != 0 &&
- (cellsRemaining < visibleItemCount - 1 || singleItem)) {
+ (cellsRemaining < visibleItemCount - 1 || singleItem || maxCellsUsed > 1)) {
float expandCount = Long.bitCount(smallestItemsAt);
if (!singleItem) {
// The items at the far edges may only expand by half in order to pin to either side.
if ((smallestItemsAt & 1) != 0) {
- expandCount -= 0.5f;
+ LayoutParams lp = (LayoutParams) getChildAt(0).getLayoutParams();
+ if (!lp.preventEdgeOffset) expandCount -= 0.5f;
}
if ((smallestItemsAt & (1 << (childCount - 1))) != 0) {
- expandCount -= 0.5f;
+ LayoutParams lp = ((LayoutParams) getChildAt(childCount - 1).getLayoutParams());
+ if (!lp.preventEdgeOffset) expandCount -= 0.5f;
}
}
@@ -232,7 +261,7 @@
// If this is one of our views, expand and measure at the larger size.
lp.extraPixels = extraPixels;
lp.expanded = true;
- if (i == 0) {
+ if (i == 0 && !lp.preventEdgeOffset) {
// First item gets part of its new padding pushed out of sight.
// The last item will get this implicitly from layout.
lp.leftMargin = -extraPixels / 2;
@@ -496,6 +525,8 @@
public int extraPixels;
@ViewDebug.ExportedProperty(category = "layout")
public boolean expandable;
+ @ViewDebug.ExportedProperty(category = "layout")
+ public boolean preventEdgeOffset;
public boolean expanded;
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index 5622b44..c30e83b 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -1057,9 +1057,8 @@
mNonActionItems.add(item);
}
}
- } else if (mActionItems.size() + mNonActionItems.size() != getVisibleItems().size()) {
- // Nobody flagged anything, but if something doesn't add up then treat everything
- // as non-action items.
+ } else {
+ // Nobody flagged anything, everything is a non-action item.
// (This happens during a first pass with no action-item presenters.)
mActionItems.clear();
mNonActionItems.clear();
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 28181ba..4efb29f 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -550,10 +550,9 @@
if (mTitleLayout != null && (flagsChanged &
(ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME)) != 0) {
- final boolean homeAsUp = (options & ActionBar.DISPLAY_HOME_AS_UP) != 0;
- final boolean titleUp = homeAsUp && !showHome;
- mTitleUpView.setVisibility(titleUp ? VISIBLE : GONE);
- mTitleLayout.setEnabled(titleUp);
+ final boolean homeAsUp = (mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0;
+ mTitleUpView.setVisibility(!showHome ? (homeAsUp ? VISIBLE : INVISIBLE) : GONE);
+ mTitleLayout.setEnabled(!showHome && homeAsUp);
}
if ((flagsChanged & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomNavView != null) {
@@ -730,10 +729,9 @@
}
final boolean homeAsUp = (mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0;
- final boolean titleUp = homeAsUp &&
- (mDisplayOptions & ActionBar.DISPLAY_SHOW_HOME) == 0;
- mTitleUpView.setVisibility(titleUp ? VISIBLE : GONE);
- mTitleLayout.setEnabled(titleUp);
+ final boolean showHome = (mDisplayOptions & ActionBar.DISPLAY_SHOW_HOME) != 0;
+ mTitleUpView.setVisibility(!showHome ? (homeAsUp ? VISIBLE : INVISIBLE) : GONE);
+ mTitleLayout.setEnabled(homeAsUp && !showHome);
}
addView(mTitleLayout);
@@ -805,7 +803,7 @@
int leftOfCenter = availableWidth / 2;
int rightOfCenter = leftOfCenter;
- View homeLayout = mExpandedActionView != null ? mExpandedHomeLayout : mHomeLayout;
+ HomeView homeLayout = mExpandedActionView != null ? mExpandedHomeLayout : mHomeLayout;
if (homeLayout.getVisibility() != GONE) {
final LayoutParams lp = homeLayout.getLayoutParams();
@@ -817,7 +815,7 @@
}
homeLayout.measure(homeWidthSpec,
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
- final int homeWidth = homeLayout.getMeasuredWidth();
+ final int homeWidth = homeLayout.getMeasuredWidth() + homeLayout.getLeftOffset();
availableWidth = Math.max(0, availableWidth - homeWidth);
leftOfCenter = Math.max(0, availableWidth - homeWidth);
}
@@ -962,9 +960,10 @@
return;
}
- View homeLayout = mExpandedActionView != null ? mExpandedHomeLayout : mHomeLayout;
+ HomeView homeLayout = mExpandedActionView != null ? mExpandedHomeLayout : mHomeLayout;
if (homeLayout.getVisibility() != GONE) {
- x += positionChild(homeLayout, x, y, contentHeight);
+ final int leftOffset = homeLayout.getLeftOffset();
+ x += positionChild(homeLayout, x + leftOffset, y, contentHeight) + leftOffset;
}
if (mExpandedActionView == null) {
@@ -1171,6 +1170,7 @@
private static class HomeView extends FrameLayout {
private View mUpView;
private ImageView mIconView;
+ private int mUpWidth;
public HomeView(Context context) {
this(context, null);
@@ -1194,15 +1194,16 @@
mIconView = (ImageView) findViewById(com.android.internal.R.id.home);
}
- public int getVerticalIconPadding() {
- return mIconView.getPaddingTop() + mIconView.getPaddingBottom();
+ public int getLeftOffset() {
+ return mUpView.getVisibility() == GONE ? mUpWidth : 0;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildWithMargins(mUpView, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams();
- int width = upLp.leftMargin + mUpView.getMeasuredWidth() + upLp.rightMargin;
+ mUpWidth = upLp.leftMargin + mUpView.getMeasuredWidth() + upLp.rightMargin;
+ int width = mUpView.getVisibility() == GONE ? 0 : mUpWidth;
int height = upLp.topMargin + mUpView.getMeasuredHeight() + upLp.bottomMargin;
measureChildWithMargins(mIconView, widthMeasureSpec, width, heightMeasureSpec, 0);
final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
diff --git a/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java b/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java
index d3baa2b..aa9fa45 100644
--- a/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java
+++ b/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java
@@ -25,6 +25,8 @@
public class TargetDrawable {
private static final String TAG = "TargetDrawable";
+ private static final boolean DEBUG = false;
+
public static final int[] STATE_ACTIVE =
{ android.R.attr.state_enabled, android.R.attr.state_active };
public static final int[] STATE_INACTIVE =
@@ -139,11 +141,13 @@
maxWidth = Math.max(maxWidth, childDrawable.getIntrinsicWidth());
maxHeight = Math.max(maxHeight, childDrawable.getIntrinsicHeight());
}
- Log.v(TAG, "union of childDrawable rects " + d + " to: " + maxWidth + "x" + maxHeight);
+ if (DEBUG) Log.v(TAG, "union of childDrawable rects " + d + " to: "
+ + maxWidth + "x" + maxHeight);
d.setBounds(0, 0, maxWidth, maxHeight);
for (int i = 0; i < d.getStateCount(); i++) {
Drawable childDrawable = d.getStateDrawable(i);
- Log.v(TAG, "sizing drawable " + childDrawable + " to: " + maxWidth + "x" + maxHeight);
+ if (DEBUG) Log.v(TAG, "sizing drawable " + childDrawable + " to: "
+ + maxWidth + "x" + maxHeight);
childDrawable.setBounds(0, 0, maxWidth, maxHeight);
}
} else if (mDrawable != null) {
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index b8f2d6f..6b5ca50 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -39,6 +39,8 @@
#include <binder/IServiceManager.h>
#include <utils/threads.h>
+#include <ScopedUtfChars.h>
+
#include <android_runtime/AndroidRuntime.h>
//#undef LOGV
@@ -444,6 +446,25 @@
return result;
}
+ void warnIfStillLive() {
+ JNIEnv* env = javavm_to_jnienv(mVM);
+ if (mObject != NULL) {
+ // Okay, something is wrong -- we have a hard reference to a live death
+ // recipient on the VM side, but the list is being torn down.
+ jclass clazz = env->GetObjectClass(mObject);
+ jmethodID getnameMethod = env->GetMethodID(clazz, "getName", NULL);
+ jstring nameString = (jstring) env->CallObjectMethod(clazz, getnameMethod);
+ if (nameString) {
+ ScopedUtfChars nameUtf(env, nameString);
+ LOGW("BinderProxy is being destroyed but the application did not call "
+ "unlinkToDeath to unlink all of its death recipients beforehand. "
+ "Releasing leaked death recipient: %s", nameUtf.c_str());
+ env->DeleteLocalRef(nameString);
+ }
+ env->DeleteLocalRef(clazz);
+ }
+ }
+
protected:
virtual ~JavaDeathRecipient()
{
@@ -478,7 +499,10 @@
// to the list are holding references on the list object. Only when they are torn
// down can the list header be destroyed.
if (mList.size() > 0) {
- LOGE("Retiring DRL %p with extant death recipients\n", this);
+ List< sp<JavaDeathRecipient> >::iterator iter;
+ for (iter = mList.begin(); iter != mList.end(); iter++) {
+ (*iter)->warnIfStillLive();
+ }
}
}
diff --git a/core/res/res/drawable-hdpi/notify_panel_notification_icon_bg.png b/core/res/res/drawable-hdpi/notify_panel_notification_icon_bg.png
index f5b762e..6f37a22 100644
--- a/core/res/res/drawable-hdpi/notify_panel_notification_icon_bg.png
+++ b/core/res/res/drawable-hdpi/notify_panel_notification_icon_bg.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_inline_error_above_holo_dark.9.png b/core/res/res/drawable-hdpi/popup_inline_error_above_holo_dark.9.png
index 61ea2b0..83b2bce 100644
--- a/core/res/res/drawable-hdpi/popup_inline_error_above_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/popup_inline_error_above_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_inline_error_above_holo_light.9.png b/core/res/res/drawable-hdpi/popup_inline_error_above_holo_light.9.png
index 83b2bce..61ea2b0 100644
--- a/core/res/res/drawable-hdpi/popup_inline_error_above_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/popup_inline_error_above_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_16_inner_holo.png b/core/res/res/drawable-hdpi/spinner_16_inner_holo.png
index 01f4278..2af7e81 100644
--- a/core/res/res/drawable-hdpi/spinner_16_inner_holo.png
+++ b/core/res/res/drawable-hdpi/spinner_16_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_20_inner_holo.png b/core/res/res/drawable-hdpi/spinner_20_inner_holo.png
index 4c9849f..74ce1ee 100644
--- a/core/res/res/drawable-hdpi/spinner_20_inner_holo.png
+++ b/core/res/res/drawable-hdpi/spinner_20_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_48_inner_holo.png b/core/res/res/drawable-hdpi/spinner_48_inner_holo.png
index 5d15e74..7220c2f 100644
--- a/core/res/res/drawable-hdpi/spinner_48_inner_holo.png
+++ b/core/res/res/drawable-hdpi/spinner_48_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_76_inner_holo.png b/core/res/res/drawable-hdpi/spinner_76_inner_holo.png
index cc8affe..633df1b 100644
--- a/core/res/res/drawable-hdpi/spinner_76_inner_holo.png
+++ b/core/res/res/drawable-hdpi/spinner_76_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/notify_panel_notification_icon_bg.png b/core/res/res/drawable-mdpi/notify_panel_notification_icon_bg.png
index f0bdfb0..c286875 100644
--- a/core/res/res/drawable-mdpi/notify_panel_notification_icon_bg.png
+++ b/core/res/res/drawable-mdpi/notify_panel_notification_icon_bg.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/popup_inline_error_above_holo_dark.9.png b/core/res/res/drawable-mdpi/popup_inline_error_above_holo_dark.9.png
index c03e658..19b153b 100644
--- a/core/res/res/drawable-mdpi/popup_inline_error_above_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/popup_inline_error_above_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/popup_inline_error_above_holo_light.9.png b/core/res/res/drawable-mdpi/popup_inline_error_above_holo_light.9.png
index 19b153b..c03e658 100644
--- a/core/res/res/drawable-mdpi/popup_inline_error_above_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/popup_inline_error_above_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_16_inner_holo.png b/core/res/res/drawable-mdpi/spinner_16_inner_holo.png
index a322a02..4f6f981 100644
--- a/core/res/res/drawable-mdpi/spinner_16_inner_holo.png
+++ b/core/res/res/drawable-mdpi/spinner_16_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_20_inner_holo.png b/core/res/res/drawable-mdpi/spinner_20_inner_holo.png
index 538788a..fab8766 100644
--- a/core/res/res/drawable-mdpi/spinner_20_inner_holo.png
+++ b/core/res/res/drawable-mdpi/spinner_20_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_48_inner_holo.png b/core/res/res/drawable-mdpi/spinner_48_inner_holo.png
index b59dc64..91a29fd 100644
--- a/core/res/res/drawable-mdpi/spinner_48_inner_holo.png
+++ b/core/res/res/drawable-mdpi/spinner_48_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_76_inner_holo.png b/core/res/res/drawable-mdpi/spinner_76_inner_holo.png
index e7d654c..a388434 100644
--- a/core/res/res/drawable-mdpi/spinner_76_inner_holo.png
+++ b/core/res/res/drawable-mdpi/spinner_76_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/notify_panel_notification_icon_bg.png b/core/res/res/drawable-xhdpi/notify_panel_notification_icon_bg.png
index 4cc515e..9128e62 100644
--- a/core/res/res/drawable-xhdpi/notify_panel_notification_icon_bg.png
+++ b/core/res/res/drawable-xhdpi/notify_panel_notification_icon_bg.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_inline_error_above_holo_dark.9.png b/core/res/res/drawable-xhdpi/popup_inline_error_above_holo_dark.9.png
index d69b772..a210f3c 100644
--- a/core/res/res/drawable-xhdpi/popup_inline_error_above_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/popup_inline_error_above_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_inline_error_above_holo_light.9.png b/core/res/res/drawable-xhdpi/popup_inline_error_above_holo_light.9.png
index a210f3c..d69b772 100644
--- a/core/res/res/drawable-xhdpi/popup_inline_error_above_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/popup_inline_error_above_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_16_inner_holo.png b/core/res/res/drawable-xhdpi/spinner_16_inner_holo.png
index d49d67a..8f2b8ae 100644
--- a/core/res/res/drawable-xhdpi/spinner_16_inner_holo.png
+++ b/core/res/res/drawable-xhdpi/spinner_16_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_20_inner_holo.png b/core/res/res/drawable-xhdpi/spinner_20_inner_holo.png
index 5eec6f3..b202d4e 100644
--- a/core/res/res/drawable-xhdpi/spinner_20_inner_holo.png
+++ b/core/res/res/drawable-xhdpi/spinner_20_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_48_inner_holo.png b/core/res/res/drawable-xhdpi/spinner_48_inner_holo.png
index 6f824026..c2df6b4 100644
--- a/core/res/res/drawable-xhdpi/spinner_48_inner_holo.png
+++ b/core/res/res/drawable-xhdpi/spinner_48_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_76_inner_holo.png b/core/res/res/drawable-xhdpi/spinner_76_inner_holo.png
index af88495..9f2044d 100644
--- a/core/res/res/drawable-xhdpi/spinner_76_inner_holo.png
+++ b/core/res/res/drawable-xhdpi/spinner_76_inner_holo.png
Binary files differ
diff --git a/core/res/res/drawable/notify_panel_notification_icon_bg_tile.xml b/core/res/res/drawable/notify_panel_notification_icon_bg_tile.xml
new file mode 100644
index 0000000..fa3c398
--- /dev/null
+++ b/core/res/res/drawable/notify_panel_notification_icon_bg_tile.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<bitmap
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:tileMode="repeat"
+ android:src="@android:drawable/notify_panel_notification_icon_bg"
+ />
diff --git a/core/res/res/layout/activity_list_item.xml b/core/res/res/layout/activity_list_item.xml
index 25d95fd..7022fe1 100644
--- a/core/res/res/layout/activity_list_item.xml
+++ b/core/res/res/layout/activity_list_item.xml
@@ -22,8 +22,8 @@
android:layout_height="wrap_content"
android:paddingTop="1dip"
android:paddingBottom="1dip"
- android:paddingLeft="6dip"
- android:paddingRight="6dip">
+ android:paddingLeft="8dip"
+ android:paddingRight="8dip">
<ImageView android:id="@+id/icon"
android:layout_width="24dip"
diff --git a/core/res/res/layout/input_method_extract_view.xml b/core/res/res/layout/input_method_extract_view.xml
index 60bc24c..a3e4961 100644
--- a/core/res/res/layout/input_method_extract_view.xml
+++ b/core/res/res/layout/input_method_extract_view.xml
@@ -40,7 +40,6 @@
android:layout_height="match_parent"
android:paddingLeft="8dip"
android:paddingRight="8dip"
- android:background="@android:drawable/keyboard_accessory_bg_landscape"
>
<android.inputmethodservice.ExtractButton android:id="@+id/inputExtractAction"
diff --git a/core/res/res/layout/list_menu_item_layout.xml b/core/res/res/layout/list_menu_item_layout.xml
index aaff4c7..93bd76b 100644
--- a/core/res/res/layout/list_menu_item_layout.xml
+++ b/core/res/res/layout/list_menu_item_layout.xml
@@ -26,8 +26,8 @@
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
- android:layout_marginLeft="6dip"
- android:layout_marginRight="6dip"
+ android:layout_marginLeft="16dip"
+ android:layout_marginRight="16dip"
android:duplicateParentState="true">
<TextView
@@ -36,7 +36,7 @@
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
- android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:singleLine="true"
android:duplicateParentState="true"
android:ellipsize="marquee"
diff --git a/core/res/res/layout/preference_widget_switch.xml b/core/res/res/layout/preference_widget_switch.xml
index ed4ed57..83ef097 100644
--- a/core/res/res/layout/preference_widget_switch.xml
+++ b/core/res/res/layout/preference_widget_switch.xml
@@ -22,5 +22,4 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="16dip"
- android:focusable="false"
- android:clickable="false" />
+ android:focusable="false" />
diff --git a/core/res/res/layout/select_dialog_item_holo.xml b/core/res/res/layout/select_dialog_item_holo.xml
index 0c700cf..3d19c06 100644
--- a/core/res/res/layout/select_dialog_item_holo.xml
+++ b/core/res/res/layout/select_dialog_item_holo.xml
@@ -27,7 +27,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
- android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:textColor="?android:attr/textColorAlertDialogListItem"
android:gravity="center_vertical"
android:paddingLeft="16dip"
diff --git a/core/res/res/layout/simple_expandable_list_item_1.xml b/core/res/res/layout/simple_expandable_list_item_1.xml
index dc3e58e..df4324b 100644
--- a/core/res/res/layout/simple_expandable_list_item_1.xml
+++ b/core/res/res/layout/simple_expandable_list_item_1.xml
@@ -19,6 +19,6 @@
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:paddingLeft="?android:attr/expandableListPreferredItemPaddingLeft"
- android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textAppearance="?android:attr/textAppearanceListItem"
android:gravity="center_vertical"
/>
diff --git a/core/res/res/layout/simple_expandable_list_item_2.xml b/core/res/res/layout/simple_expandable_list_item_2.xml
index b48b444..c0935fa 100644
--- a/core/res/res/layout/simple_expandable_list_item_2.xml
+++ b/core/res/res/layout/simple_expandable_list_item_2.xml
@@ -27,7 +27,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dip"
- android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textAppearance="?android:attr/textAppearanceListItem"
/>
<TextView android:id="@android:id/text2"
diff --git a/core/res/res/layout/simple_list_item_1.xml b/core/res/res/layout/simple_list_item_1.xml
index c9c77a5..252e006 100644
--- a/core/res/res/layout/simple_list_item_1.xml
+++ b/core/res/res/layout/simple_list_item_1.xml
@@ -18,8 +18,9 @@
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textAppearance="?android:attr/textAppearanceListItem"
android:gravity="center_vertical"
- android:paddingLeft="6dip"
+ android:paddingLeft="8dip"
+ android:paddingRight="8dip"
android:minHeight="?android:attr/listPreferredItemHeight"
/>
diff --git a/core/res/res/layout/simple_list_item_2.xml b/core/res/res/layout/simple_list_item_2.xml
index c87922c..9b6c62a 100644
--- a/core/res/res/layout/simple_list_item_2.xml
+++ b/core/res/res/layout/simple_list_item_2.xml
@@ -24,9 +24,9 @@
<TextView android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginLeft="6dip"
- android:layout_marginTop="6dip"
- android:textAppearance="?android:attr/textAppearanceLarge"
+ android:layout_marginLeft="8dip"
+ android:layout_marginTop="8dip"
+ android:textAppearance="?android:attr/textAppearanceListItem"
/>
<TextView android:id="@android:id/text2"
diff --git a/core/res/res/layout/simple_list_item_activated_1.xml b/core/res/res/layout/simple_list_item_activated_1.xml
index 8416df2..d60f93b 100644
--- a/core/res/res/layout/simple_list_item_activated_1.xml
+++ b/core/res/res/layout/simple_list_item_activated_1.xml
@@ -18,7 +18,7 @@
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textAppearance="?android:attr/textAppearanceListItem"
android:gravity="center_vertical"
android:background="?android:attr/activatedBackgroundIndicator"
android:minHeight="?android:attr/listPreferredItemHeight"
diff --git a/core/res/res/layout/simple_list_item_activated_2.xml b/core/res/res/layout/simple_list_item_activated_2.xml
index 2ffbf02..5be5c92 100644
--- a/core/res/res/layout/simple_list_item_activated_2.xml
+++ b/core/res/res/layout/simple_list_item_activated_2.xml
@@ -27,8 +27,8 @@
<TextView android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginLeft="6dip"
- android:layout_marginTop="6dip"
+ android:layout_marginLeft="8dip"
+ android:layout_marginTop="8dip"
android:textAppearance="?android:attr/textAppearanceLarge"
/>
diff --git a/core/res/res/layout/simple_list_item_checked.xml b/core/res/res/layout/simple_list_item_checked.xml
index 5f99044..79d3a18 100644
--- a/core/res/res/layout/simple_list_item_checked.xml
+++ b/core/res/res/layout/simple_list_item_checked.xml
@@ -18,9 +18,9 @@
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
- android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textAppearance="?android:attr/textAppearanceListItem"
android:gravity="center_vertical"
android:checkMark="?android:attr/textCheckMark"
- android:paddingLeft="6dip"
- android:paddingRight="6dip"
+ android:paddingLeft="8dip"
+ android:paddingRight="8dip"
/>
diff --git a/core/res/res/layout/simple_list_item_multiple_choice.xml b/core/res/res/layout/simple_list_item_multiple_choice.xml
index 05c66f3..0305427 100644
--- a/core/res/res/layout/simple_list_item_multiple_choice.xml
+++ b/core/res/res/layout/simple_list_item_multiple_choice.xml
@@ -18,9 +18,9 @@
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
- android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textAppearance="?android:attr/textAppearanceListItem"
android:gravity="center_vertical"
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
- android:paddingLeft="6dip"
- android:paddingRight="6dip"
+ android:paddingLeft="8dip"
+ android:paddingRight="8dip"
/>
diff --git a/core/res/res/layout/simple_list_item_single_choice.xml b/core/res/res/layout/simple_list_item_single_choice.xml
index 27afd1d..ac4a4a8 100644
--- a/core/res/res/layout/simple_list_item_single_choice.xml
+++ b/core/res/res/layout/simple_list_item_single_choice.xml
@@ -18,9 +18,9 @@
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
- android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textAppearance="?android:attr/textAppearanceListItem"
android:gravity="center_vertical"
android:checkMark="?android:attr/listChoiceIndicatorSingle"
- android:paddingLeft="6dip"
- android:paddingRight="6dip"
+ android:paddingLeft="8dip"
+ android:paddingRight="8dip"
/>
diff --git a/core/res/res/layout/simple_selectable_list_item.xml b/core/res/res/layout/simple_selectable_list_item.xml
index 518bcd0..6ce22d6 100644
--- a/core/res/res/layout/simple_selectable_list_item.xml
+++ b/core/res/res/layout/simple_selectable_list_item.xml
@@ -18,9 +18,9 @@
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
- android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textAppearance="?android:attr/textAppearanceListItem"
android:gravity="center_vertical"
android:background="?android:attr/listChoiceBackgroundIndicator"
- android:paddingLeft="6dip"
- android:paddingRight="9dip"
+ android:paddingLeft="8dip"
+ android:paddingRight="8dip"
/>
diff --git a/core/res/res/layout/status_bar_latest_event_content.xml b/core/res/res/layout/status_bar_latest_event_content.xml
index 0dc6741..ec1bc81 100644
--- a/core/res/res/layout/status_bar_latest_event_content.xml
+++ b/core/res/res/layout/status_bar_latest_event_content.xml
@@ -6,7 +6,7 @@
<ImageView android:id="@+id/icon"
android:layout_width="@dimen/notification_large_icon_width"
android:layout_height="@dimen/notification_large_icon_height"
- android:background="@drawable/notify_panel_notification_icon_bg"
+ android:background="@android:drawable/notify_panel_notification_icon_bg_tile"
android:scaleType="center"
/>
<include layout="@layout/status_bar_latest_event_content_large_icon"
diff --git a/core/res/res/values-sw600dp/bools.xml b/core/res/res/values-sw600dp/bools.xml
index a453ac1..2097049 100644
--- a/core/res/res/values-sw600dp/bools.xml
+++ b/core/res/res/values-sw600dp/bools.xml
@@ -18,4 +18,5 @@
<bool name="preferences_prefer_dual_pane">true</bool>
<bool name="show_ongoing_ime_switcher">false</bool>
<bool name="action_bar_expanded_action_views_exclusive">false</bool>
+ <bool name="target_honeycomb_needs_options_menu">false</bool>
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index aa3397f..bb61c72 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -236,6 +236,11 @@
<!-- The list item height for search results. @hide -->
<attr name="searchResultListItemHeight" format="dimension" />
+ <!-- The preferred TextAppearance for the primary text of list items. -->
+ <attr name="textAppearanceListItem" format="reference" />
+ <!-- The preferred TextAppearance for the primary text of small list items. -->
+ <attr name="textAppearanceListItemSmall" format="reference" />
+
<!-- The drawable for the list divider. -->
<attr name="listDivider" format="reference" />
<!-- The list divider used in alert dialogs. -->
@@ -3929,15 +3934,14 @@
<declare-styleable name="Animation">
<!-- Defines the interpolator used to smooth the animation movement in time. -->
<attr name="interpolator" />
- <!-- When set to true, fillAfter is taken into account. -->
+ <!-- When set to true, the value of fillBefore is taken into account. -->
<attr name="fillEnabled" format="boolean" />
- <!-- When set to true, the animation transformation is applied before the animation has
- started. The default value is true. If fillEnabled is not set to true, fillBefore
- is assumed to be true. -->
+ <!-- When set to true or when fillEnabled is not set to true, the animation transformation
+ is applied before the animation has started. The default value is true. -->
<attr name="fillBefore" format="boolean" />
<!-- When set to true, the animation transformation is applied after the animation is
- over. The default value is false. If fillEnabled is not set to true and the animation
- is not set on a View, fillAfter is assumed to be true. -->
+ over. The default value is false. If fillEnabled is not set to true and the
+ animation is not set on a View, fillAfter is assumed to be true.-->
<attr name="fillAfter" format="boolean" />
<!-- Amount of time (in milliseconds) for the animation to run. -->
<attr name="duration" />
@@ -4095,7 +4099,6 @@
<declare-styleable name="Animator">
<!-- Defines the interpolator used to smooth the animation movement in time. -->
<attr name="interpolator" />
- <!-- When set to true, fillAfter is taken into account. -->
<!-- Amount of time (in milliseconds) for the animation to run. -->
<attr name="duration" />
<!-- Delay in milliseconds before the animation runs, once start time is reached. -->
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index 6e4db5e..87a98e2 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -20,4 +20,5 @@
<bool name="preferences_prefer_dual_pane">false</bool>
<bool name="show_ongoing_ime_switcher">true</bool>
<bool name="action_bar_expanded_action_views_exclusive">true</bool>
+ <bool name="target_honeycomb_needs_options_menu">true</bool>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 74989e6..8fbb09e 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -57,8 +57,9 @@
<!-- Flag indicating whether the surface flinger is inefficient
at performing a blur. Used by parts of the UI to turn off
- the blur effect where it isn't worth the performance hit. -->
- <bool name="config_sf_slowBlur">false</bool>
+ the blur effect where it isn't worth the performance hit.
+ As of Honeycomb, blurring is not supported anymore. -->
+ <bool name="config_sf_slowBlur">true</bool>
<!-- The duration (in milliseconds) of a short animation. -->
<integer name="config_shortAnimTime">200</integer>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 730d971..a6bf1e0 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1792,6 +1792,9 @@
<public type="attr" name="actionBarItemBackground" />
<public type="attr" name="actionModeSplitBackground" />
+ <public type="attr" name="textAppearanceListItem" />
+ <public type="attr" name="textAppearanceListItemSmall" />
+
<public type="style" name="TextAppearance.SuggestionHighlight" />
<public type="style" name="Theme.Holo.Light.DarkActionBar" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index c80923d..8eaac8c 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3163,6 +3163,8 @@
<string name="data_usage_4g_limit_title">4G data disabled</string>
<!-- Notification title when mobile data usage has exceeded limit threshold, and has been disabled. [CHAR LIMIT=32] -->
<string name="data_usage_mobile_limit_title">Mobile data disabled</string>
+ <!-- Notification title when Wi-Fi data usage has exceeded limit threshold, and has been disabled. [CHAR LIMIT=32] -->
+ <string name="data_usage_wifi_limit_title">Wi-Fi data disabled</string>
<!-- Notification body when data usage has exceeded limit threshold, and has been disabled. [CHAR LIMIT=32] -->
<string name="data_usage_limit_body">Touch to enable</string>
@@ -3172,6 +3174,8 @@
<string name="data_usage_4g_limit_snoozed_title">4G data limit exceeded</string>
<!-- Notification title when mobile data usage has exceeded limit threshold. [CHAR LIMIT=32] -->
<string name="data_usage_mobile_limit_snoozed_title">Mobile data limit exceeded</string>
+ <!-- Notification title when Wi-Fi data usage has exceeded limit threshold. [CHAR LIMIT=32] -->
+ <string name="data_usage_wifi_limit_snoozed_title">Wi-Fi data limit exceeded</string>
<!-- Notification body when data usage has exceeded limit threshold. [CHAR LIMIT=32] -->
<string name="data_usage_limit_snoozed_body"><xliff:g id="size" example="3.8GB">%s</xliff:g> over specified limit</string>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 786cdb5..903fc04 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -122,6 +122,8 @@
<item name="listPreferredItemHeightSmall">?android:attr/listPreferredItemHeight</item>
<item name="listPreferredItemHeightLarge">?android:attr/listPreferredItemHeight</item>
<item name="dropdownListPreferredItemHeight">?android:attr/listPreferredItemHeight</item>
+ <item name="textAppearanceListItem">?android:attr/textAppearanceLarge</item>
+ <item name="textAppearanceListItemSmall">?android:attr/textAppearanceLarge</item>
<!-- @hide -->
<item name="searchResultListItemHeight">58dip</item>
@@ -715,9 +717,9 @@
{@link android.inputmethodservice.InputMethodService} class.
this inherits from Theme.Panel, but sets up IME appropriate animations
and a few custom attributes. -->
- <style name="Theme.Holo.InputMethod" parent="Theme.Holo.Panel">
+ <style name="Theme.Holo.InputMethod" parent="Theme.Holo.Light.Panel">
<item name="android:windowAnimationStyle">@android:style/Animation.InputMethod</item>
- <item name="android:imeFullscreenBackground">@android:drawable/input_method_fullscreen_background_holo</item>
+ <item name="android:imeFullscreenBackground">@android:drawable/screen_background_selector_light</item>
<item name="android:imeExtractEnterAnimation">@android:anim/input_method_extract_enter</item>
<item name="android:imeExtractExitAnimation">@android:anim/input_method_extract_exit</item>
</style>
@@ -920,6 +922,7 @@
<item name="listPreferredItemHeightSmall">48dip</item>
<item name="listPreferredItemHeightLarge">80dip</item>
<item name="dropdownListPreferredItemHeight">?android:attr/listPreferredItemHeightSmall</item>
+ <item name="textAppearanceListItemSmall">?android:attr/textAppearanceMedium</item>
<!-- @hide -->
<item name="searchResultListItemHeight">58dip</item>
@@ -1222,6 +1225,7 @@
<item name="listPreferredItemHeightSmall">48dip</item>
<item name="listPreferredItemHeightLarge">80dip</item>
<item name="dropdownListPreferredItemHeight">?android:attr/listPreferredItemHeightSmall</item>
+ <item name="textAppearanceListItemSmall">?android:attr/textAppearanceMedium</item>
<!-- @hide -->
<item name="searchResultListItemHeight">58dip</item>
diff --git a/data/sounds/AudioPackage7.mk b/data/sounds/AudioPackage7.mk
new file mode 100755
index 0000000..44dd899
--- /dev/null
+++ b/data/sounds/AudioPackage7.mk
@@ -0,0 +1,64 @@
+#
+# Audio Package 7 - Tuna
+#
+# Include this file in a product makefile to include these audio files
+#
+#
+
+LOCAL_PATH:= frameworks/base/data/sounds
+
+PRODUCT_COPY_FILES += \
+ $(LOCAL_PATH)/alarms/ogg/Cesium.ogg:system/media/audio/alarms/Cesium.ogg \
+ $(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:system/media/audio/alarms/Plutonium.ogg \
+ $(LOCAL_PATH)/alarms/ogg/Scandium.ogg:system/media/audio/alarms/Scandium.ogg \
+ $(LOCAL_PATH)/alarms/ogg/Curium.ogg:system/media/audio/alarms/Curium.ogg \
+ $(LOCAL_PATH)/alarms/ogg/Fermium.ogg:system/media/audio/alarms/Fermium.ogg \
+ $(LOCAL_PATH)/alarms/ogg/Nobelium.ogg:system/media/audio/alarms/Nobelium.ogg \
+ $(LOCAL_PATH)/alarms/ogg/Copernicium.ogg:system/media/audio/alarms/Copernicium.ogg \
+ $(LOCAL_PATH)/alarms/ogg/Hassium.ogg:system/media/audio/alarms/Hassium.ogg \
+ $(LOCAL_PATH)/alarms/ogg/Neptunium.ogg:system/media/audio/alarms/Neptunium.ogg \
+ $(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \
+ $(LOCAL_PATH)/effects/ogg/KeypressStandard_24.ogg:system/media/audio/ui/KeypressStandard.ogg \
+ $(LOCAL_PATH)/effects/ogg/KeypressSpacebar_24.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
+ $(LOCAL_PATH)/effects/ogg/KeypressDelete_24.ogg:system/media/audio/ui/KeypressDelete.ogg \
+ $(LOCAL_PATH)/effects/ogg/KeypressReturn_24.ogg:system/media/audio/ui/KeypressReturn.ogg \
+ $(LOCAL_PATH)/effects/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
+ $(LOCAL_PATH)/effects/ogg/CameraShutter.ogg:system/media/audio/ui/camera_click.ogg \
+ $(LOCAL_PATH)/effects/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
+ $(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
+ $(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
+ $(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \
+ $(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
+ $(LOCAL_PATH)/notifications/ogg/Altair.ogg:system/media/audio/notifications/Altair.ogg \
+ $(LOCAL_PATH)/notifications/ogg/Antares.ogg:system/media/audio/notifications/Antares.ogg \
+ $(LOCAL_PATH)/notifications/ogg/Deneb.ogg:system/media/audio/notifications/Deneb.ogg \
+ $(LOCAL_PATH)/notifications/ogg/Lalande.ogg:system/media/audio/notifications/Lalande.ogg \
+ $(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
+ $(LOCAL_PATH)/notifications/ogg/Proxima.ogg:system/media/audio/notifications/Proxima.ogg \
+ $(LOCAL_PATH)/notifications/ogg/Betelgeuse.ogg:system/media/audio/notifications/Betelgeuse.ogg \
+ $(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
+ $(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:system/media/audio/notifications/Upsilon.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/Bootes.ogg:system/media/audio/ringtones/Bootes.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:system/media/audio/ringtones/CanisMajor.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/Carina.ogg:system/media/audio/ringtones/Carina.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/Cassiopeia.ogg:system/media/audio/ringtones/Cassiopeia.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/Draco.ogg:system/media/audio/ringtones/Draco.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/Hydra.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/Lyra.ogg:system/media/audio/ringtones/Lyra.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:system/media/audio/ringtones/Perseus.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:system/media/audio/ringtones/Rigel.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/Testudo.ogg:system/media/audio/ringtones/Testudo.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:system/media/audio/ringtones/UrsaMinor.ogg \
+ $(LOCAL_PATH)/ringtones/ogg/Vespa.ogg:system/media/audio/ringtones/Vespa.ogg
diff --git a/data/sounds/alarms/ogg/Copernicium.ogg b/data/sounds/alarms/ogg/Copernicium.ogg
new file mode 100644
index 0000000..c619e8b
--- /dev/null
+++ b/data/sounds/alarms/ogg/Copernicium.ogg
Binary files differ
diff --git a/data/sounds/alarms/ogg/Curium.ogg b/data/sounds/alarms/ogg/Curium.ogg
new file mode 100644
index 0000000..ebce391
--- /dev/null
+++ b/data/sounds/alarms/ogg/Curium.ogg
Binary files differ
diff --git a/data/sounds/alarms/ogg/Fermium.ogg b/data/sounds/alarms/ogg/Fermium.ogg
new file mode 100644
index 0000000..6132565
--- /dev/null
+++ b/data/sounds/alarms/ogg/Fermium.ogg
Binary files differ
diff --git a/data/sounds/alarms/ogg/Hassium.ogg b/data/sounds/alarms/ogg/Hassium.ogg
new file mode 100644
index 0000000..408b7c2
--- /dev/null
+++ b/data/sounds/alarms/ogg/Hassium.ogg
Binary files differ
diff --git a/data/sounds/alarms/ogg/Neptunium.ogg b/data/sounds/alarms/ogg/Neptunium.ogg
new file mode 100644
index 0000000..058e2db
--- /dev/null
+++ b/data/sounds/alarms/ogg/Neptunium.ogg
Binary files differ
diff --git a/data/sounds/alarms/ogg/Nobelium.ogg b/data/sounds/alarms/ogg/Nobelium.ogg
new file mode 100644
index 0000000..33878c9
--- /dev/null
+++ b/data/sounds/alarms/ogg/Nobelium.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/CameraShutter.ogg b/data/sounds/effects/ogg/CameraShutter.ogg
new file mode 100644
index 0000000..1b67dac
--- /dev/null
+++ b/data/sounds/effects/ogg/CameraShutter.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/Dock.ogg b/data/sounds/effects/ogg/Dock.ogg
old mode 100755
new mode 100644
index a1c1f2c..caa8eeb
--- a/data/sounds/effects/ogg/Dock.ogg
+++ b/data/sounds/effects/ogg/Dock.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/KeypressDelete_24.ogg b/data/sounds/effects/ogg/KeypressDelete_24.ogg
new file mode 100644
index 0000000..2503c3e
--- /dev/null
+++ b/data/sounds/effects/ogg/KeypressDelete_24.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/KeypressReturn_24.ogg b/data/sounds/effects/ogg/KeypressReturn_24.ogg
new file mode 100644
index 0000000..342eb12
--- /dev/null
+++ b/data/sounds/effects/ogg/KeypressReturn_24.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/KeypressSpacebar_24.ogg b/data/sounds/effects/ogg/KeypressSpacebar_24.ogg
new file mode 100644
index 0000000..9f17dd2
--- /dev/null
+++ b/data/sounds/effects/ogg/KeypressSpacebar_24.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/KeypressStandard_24.ogg b/data/sounds/effects/ogg/KeypressStandard_24.ogg
new file mode 100644
index 0000000..80d7d6d1
--- /dev/null
+++ b/data/sounds/effects/ogg/KeypressStandard_24.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/Lock.ogg b/data/sounds/effects/ogg/Lock.ogg
old mode 100755
new mode 100644
index deeba68..471258a
--- a/data/sounds/effects/ogg/Lock.ogg
+++ b/data/sounds/effects/ogg/Lock.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/Media_Volume.ogg b/data/sounds/effects/ogg/Media_Volume.ogg
index 88db9d9..b06656f 100644
--- a/data/sounds/effects/ogg/Media_Volume.ogg
+++ b/data/sounds/effects/ogg/Media_Volume.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/Undock.ogg b/data/sounds/effects/ogg/Undock.ogg
old mode 100755
new mode 100644
index 91e410e..28918f7
--- a/data/sounds/effects/ogg/Undock.ogg
+++ b/data/sounds/effects/ogg/Undock.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/Unlock.ogg b/data/sounds/effects/ogg/Unlock.ogg
old mode 100755
new mode 100644
index ac50288..1cd537b
--- a/data/sounds/effects/ogg/Unlock.ogg
+++ b/data/sounds/effects/ogg/Unlock.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/VideoRecord.ogg b/data/sounds/effects/ogg/VideoRecord.ogg
index 7afe9e6..28455c9 100644
--- a/data/sounds/effects/ogg/VideoRecord.ogg
+++ b/data/sounds/effects/ogg/VideoRecord.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Altair.ogg b/data/sounds/notifications/ogg/Altair.ogg
new file mode 100755
index 0000000..d84b59e
--- /dev/null
+++ b/data/sounds/notifications/ogg/Altair.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Antares.ogg b/data/sounds/notifications/ogg/Antares.ogg
new file mode 100755
index 0000000..9d60917
--- /dev/null
+++ b/data/sounds/notifications/ogg/Antares.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Betelgeuse.ogg b/data/sounds/notifications/ogg/Betelgeuse.ogg
new file mode 100644
index 0000000..83c7722
--- /dev/null
+++ b/data/sounds/notifications/ogg/Betelgeuse.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Deneb.ogg b/data/sounds/notifications/ogg/Deneb.ogg
new file mode 100755
index 0000000..e58b3b6
--- /dev/null
+++ b/data/sounds/notifications/ogg/Deneb.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Hojus.ogg b/data/sounds/notifications/ogg/Hojus.ogg
new file mode 100644
index 0000000..fc8f73f
--- /dev/null
+++ b/data/sounds/notifications/ogg/Hojus.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Lalande.ogg b/data/sounds/notifications/ogg/Lalande.ogg
new file mode 100755
index 0000000..b6e253a
--- /dev/null
+++ b/data/sounds/notifications/ogg/Lalande.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Mira.ogg b/data/sounds/notifications/ogg/Mira.ogg
new file mode 100644
index 0000000..f21e3c4
--- /dev/null
+++ b/data/sounds/notifications/ogg/Mira.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Proxima.ogg b/data/sounds/notifications/ogg/Proxima.ogg
new file mode 100644
index 0000000..235b5ca
--- /dev/null
+++ b/data/sounds/notifications/ogg/Proxima.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Upsilon.ogg b/data/sounds/notifications/ogg/Upsilon.ogg
new file mode 100644
index 0000000..036dcad
--- /dev/null
+++ b/data/sounds/notifications/ogg/Upsilon.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ogg/Cassiopeia.ogg b/data/sounds/ringtones/ogg/Cassiopeia.ogg
index 942d4e4..61c4d27 100644
--- a/data/sounds/ringtones/ogg/Cassiopeia.ogg
+++ b/data/sounds/ringtones/ogg/Cassiopeia.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ogg/Lyra.ogg b/data/sounds/ringtones/ogg/Lyra.ogg
index e4bf37a..b7f740d 100644
--- a/data/sounds/ringtones/ogg/Lyra.ogg
+++ b/data/sounds/ringtones/ogg/Lyra.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ogg/Sceptrum.ogg b/data/sounds/ringtones/ogg/Sceptrum.ogg
index a006afe..89d64d70 100644
--- a/data/sounds/ringtones/ogg/Sceptrum.ogg
+++ b/data/sounds/ringtones/ogg/Sceptrum.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ogg/Solarium.ogg b/data/sounds/ringtones/ogg/Solarium.ogg
index 108ba11..361367a 100644
--- a/data/sounds/ringtones/ogg/Solarium.ogg
+++ b/data/sounds/ringtones/ogg/Solarium.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ogg/UrsaMinor.ogg b/data/sounds/ringtones/ogg/UrsaMinor.ogg
index 5591d73..a80801d 100644
--- a/data/sounds/ringtones/ogg/UrsaMinor.ogg
+++ b/data/sounds/ringtones/ogg/UrsaMinor.ogg
Binary files differ
diff --git a/data/sounds/ringtones/ogg/Vespa.ogg b/data/sounds/ringtones/ogg/Vespa.ogg
index 6fb8ebd..1f75ec8 100644
--- a/data/sounds/ringtones/ogg/Vespa.ogg
+++ b/data/sounds/ringtones/ogg/Vespa.ogg
Binary files differ
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index d62fd67..f3b62ec 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -93,7 +93,7 @@
* @param texName the OpenGL texture object name (e.g. generated via glGenTextures)
*/
public SurfaceTexture(int texName) {
- this(texName, true);
+ this(texName, false);
}
/**
@@ -104,6 +104,8 @@
* When the image stream comes from OpenGL, SurfaceTexture may run in the synchronous
* mode where the producer side may be blocked to avoid skipping frames. To avoid the
* thread block, set allowSynchronousMode to false.
+ *
+ * @hide
*/
public SurfaceTexture(int texName, boolean allowSynchronousMode) {
Looper looper;
diff --git a/include/media/stagefright/SurfaceMediaSource.h b/include/media/stagefright/SurfaceMediaSource.h
index 1affb8a..74d54d1 100644
--- a/include/media/stagefright/SurfaceMediaSource.h
+++ b/include/media/stagefright/SurfaceMediaSource.h
@@ -182,9 +182,9 @@
protected:
- // freeAllBuffers frees the resources (both GraphicBuffer and EGLImage) for
+ // freeAllBuffersLocked frees the resources (both GraphicBuffer and EGLImage) for
// all slots.
- void freeAllBuffers();
+ void freeAllBuffersLocked();
static bool isExternalFormat(uint32_t format);
private:
@@ -337,8 +337,15 @@
// Set to a default of 30 fps if not specified by the client side
int32_t mFrameRate;
- // mStarted is a flag to check if the recording has started
- bool mStarted;
+ // mStopped is a flag to check if the recording is going on
+ bool mStopped;
+
+ // mNumFramesReceived indicates the number of frames recieved from
+ // the client side
+ int mNumFramesReceived;
+ // mNumFramesEncoded indicates the number of frames passed on to the
+ // encoder
+ int mNumFramesEncoded;
// mFrameAvailableCondition condition used to indicate whether there
// is a frame available for dequeuing
diff --git a/include/media/stagefright/openmax/OMX_IVCommon.h b/include/media/stagefright/openmax/OMX_IVCommon.h
index 97170d7..65b6339 100644
--- a/include/media/stagefright/openmax/OMX_IVCommon.h
+++ b/include/media/stagefright/openmax/OMX_IVCommon.h
@@ -154,7 +154,8 @@
* Gralloc Buffers.
* FIXME: In the process of reserving some enum values for
* Android-specific OMX IL colorformats. Change this enum to
- * an acceptable range once that is done.*/
+ * an acceptable range once that is done.
+ * */
OMX_COLOR_FormatAndroidOpaque = 0x7F000001,
OMX_TI_COLOR_FormatYUV420PackedSemiPlanar = 0x7F000100,
OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00,
diff --git a/include/ui/GraphicBuffer.h b/include/ui/GraphicBuffer.h
index 370253a..b9deafc 100644
--- a/include/ui/GraphicBuffer.h
+++ b/include/ui/GraphicBuffer.h
@@ -62,6 +62,7 @@
USAGE_HW_TEXTURE = GRALLOC_USAGE_HW_TEXTURE,
USAGE_HW_RENDER = GRALLOC_USAGE_HW_RENDER,
USAGE_HW_2D = GRALLOC_USAGE_HW_2D,
+ USAGE_HW_COMPOSER = GRALLOC_USAGE_HW_COMPOSER,
USAGE_HW_MASK = GRALLOC_USAGE_HW_MASK
};
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 54d04aa..ff45fa3 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -351,13 +351,13 @@
// ----------------------------------------------------------------------------
-status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn) {
+status_t Surface::lock(SurfaceInfo* other, Region* inOutDirtyRegion) {
ANativeWindow_Buffer outBuffer;
ARect temp;
ARect* inOutDirtyBounds = NULL;
- if (dirtyIn) {
- temp = dirtyIn->getBounds();
+ if (inOutDirtyRegion) {
+ temp = inOutDirtyRegion->getBounds();
inOutDirtyBounds = &temp;
}
@@ -371,6 +371,11 @@
other->format = uint32_t(outBuffer.format);
other->bits = outBuffer.bits;
}
+
+ if (inOutDirtyRegion) {
+ inOutDirtyRegion->set( static_cast<Rect const&>(temp) );
+ }
+
return err;
}
diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp
index 36bbdf0..d6ab0da 100644
--- a/libs/rs/rsElement.cpp
+++ b/libs/rs/rsElement.cpp
@@ -284,6 +284,15 @@
}
}
+Element::Builder::Builder() {
+ const uint32_t initialCapacity = 32;
+ mBuilderElementRefs.setCapacity(initialCapacity);
+ mBuilderElements.setCapacity(initialCapacity);
+ mBuilderNameStrings.setCapacity(initialCapacity);
+ mBuilderNameLengths.setCapacity(initialCapacity);
+ mBuilderArrays.setCapacity(initialCapacity);
+}
+
void Element::Builder::add(const Element *e, const char *nameStr, uint32_t arraySize) {
mBuilderElementRefs.push(ObjectBaseRef<const Element>(e));
mBuilderElements.push(e);
@@ -303,41 +312,12 @@
ElementState::ElementState() {
- const uint32_t initialCapacity = 32;
- mBuilderElements.setCapacity(initialCapacity);
- mBuilderNameStrings.setCapacity(initialCapacity);
- mBuilderNameLengths.setCapacity(initialCapacity);
- mBuilderArrays.setCapacity(initialCapacity);
}
ElementState::~ElementState() {
rsAssert(!mElements.size());
}
-void ElementState::elementBuilderBegin() {
- mBuilderElements.clear();
- mBuilderNameStrings.clear();
- mBuilderNameLengths.clear();
- mBuilderArrays.clear();
-}
-
-void ElementState::elementBuilderAdd(const Element *e, const char *nameStr, uint32_t arraySize) {
- mBuilderElements.push(e);
- mBuilderNameStrings.push(nameStr);
- mBuilderNameLengths.push(strlen(nameStr));
- mBuilderArrays.push(arraySize);
-
-}
-
-const Element *ElementState::elementBuilderCreate(Context *rsc) {
- return Element::create(rsc, mBuilderElements.size(),
- &(mBuilderElements.editArray()[0]),
- &(mBuilderNameStrings.editArray()[0]),
- mBuilderNameLengths.editArray(),
- mBuilderArrays.editArray());
-}
-
-
/////////////////////////////////////////
//
diff --git a/libs/rs/rsElement.h b/libs/rs/rsElement.h
index c3ef250..bfdec53 100644
--- a/libs/rs/rsElement.h
+++ b/libs/rs/rsElement.h
@@ -30,6 +30,7 @@
public:
class Builder {
public:
+ Builder();
void add(const Element *e, const char *nameStr, uint32_t arraySize);
ObjectBaseRef<const Element> create(Context *rsc);
private:
@@ -135,17 +136,8 @@
ElementState();
~ElementState();
- void elementBuilderBegin();
- void elementBuilderAdd(const Element *e, const char *nameStr, uint32_t arraySize);
- const Element *elementBuilderCreate(Context *rsc);
-
// Cache of all existing elements.
Vector<Element *> mElements;
-private:
- Vector<const Element *> mBuilderElements;
- Vector<const char*> mBuilderNameStrings;
- Vector<size_t> mBuilderNameLengths;
- Vector<uint32_t> mBuilderArrays;
};
diff --git a/libs/rs/rsLocklessFifo.cpp b/libs/rs/rsLocklessFifo.cpp
index 02a76ab..4d02269 100644
--- a/libs/rs/rsLocklessFifo.cpp
+++ b/libs/rs/rsLocklessFifo.cpp
@@ -21,11 +21,11 @@
using namespace android;
using namespace android::renderscript;
-LocklessCommandFifo::LocklessCommandFifo() : mBuffer(0) {
+LocklessCommandFifo::LocklessCommandFifo() : mBuffer(0), mInitialized(false) {
}
LocklessCommandFifo::~LocklessCommandFifo() {
- if (!mInShutdown) {
+ if (!mInShutdown && mInitialized) {
shutdown();
}
if (mBuffer) {
@@ -58,6 +58,7 @@
mGet = mBuffer;
mEnd = mBuffer + (sizeInBytes) - 1;
//dumpState("init");
+ mInitialized = true;
return true;
}
diff --git a/libs/rs/rsLocklessFifo.h b/libs/rs/rsLocklessFifo.h
index 4962ef6..fa53d40 100644
--- a/libs/rs/rsLocklessFifo.h
+++ b/libs/rs/rsLocklessFifo.h
@@ -47,6 +47,7 @@
uint8_t * mEnd;
uint8_t mSize;
bool mInShutdown;
+ bool mInitialized;
Signal mSignalToWorker;
Signal mSignalToControl;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index f9efd3c..25c4200 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1715,136 +1715,47 @@
}
}
- /**
- * Acts as a proxy between AudioService and the RemoteControlClient
- */
- private IRemoteControlClientDispatcher mRcClientDispatcher =
- new IRemoteControlClientDispatcher.Stub() {
-
- public String getMetadataStringForClient(String clientName, int field) {
- RemoteControlClient realClient;
- synchronized(mRcClientMap) {
- realClient = mRcClientMap.get(clientName);
- }
- if (realClient != null) {
- return realClient.getMetadataString(field);
- } else {
- return null;
- }
- }
-
- public int getPlaybackStateForClient(String clientName) {
- RemoteControlClient realClient;
- synchronized(mRcClientMap) {
- realClient = mRcClientMap.get(clientName);
- }
- if (realClient != null) {
- return realClient.getPlaybackState();
- } else {
- return 0;
- }
- }
-
- public int getTransportControlFlagsForClient(String clientName) {
- RemoteControlClient realClient;
- synchronized(mRcClientMap) {
- realClient = mRcClientMap.get(clientName);
- }
- if (realClient != null) {
- return realClient.getTransportControlFlags();
- } else {
- return 0;
- }
- }
-
- public Bitmap getAlbumArtForClient(String clientName, int maxWidth, int maxHeight) {
- RemoteControlClient realClient;
- synchronized(mRcClientMap) {
- realClient = mRcClientMap.get(clientName);
- }
- if (realClient != null) {
- return realClient.getAlbumArt(maxWidth, maxHeight);
- } else {
- return null;
- }
- }
- };
-
- private HashMap<String, RemoteControlClient> mRcClientMap =
- new HashMap<String, RemoteControlClient>();
-
- private String getIdForRcClient(RemoteControlClient client) {
- // client is guaranteed to be non-null
- return client.toString();
- }
/**
* @hide
+ * CANDIDATE FOR SDK
* Registers the remote control client for providing information to display on the remote
* controls.
- * @param eventReceiver identifier of a {@link android.content.BroadcastReceiver}
- * that will receive the media button intent, and associated with the remote control
- * client. This method has no effect if
- * {@link #registerMediaButtonEventReceiver(ComponentName)} hasn't been called
- * with the same eventReceiver, or if
- * {@link #unregisterMediaButtonEventReceiver(ComponentName)} has been called.
- * @param rcClient the remote control client associated with the event receiver, responsible
+ * @param rcClient the remote control client associated responsible
* for providing the information to display on the remote control.
*/
- public void registerRemoteControlClient(ComponentName eventReceiver,
- RemoteControlClient rcClient) {
- if ((eventReceiver == null) || (rcClient == null)) {
+ public void registerRemoteControlClient(RemoteControlClient rcClient) {
+ if ((rcClient == null) || (rcClient.getRcEventReceiver() == null)) {
return;
}
- String clientKey = getIdForRcClient(rcClient);
- synchronized(mRcClientMap) {
- if (mRcClientMap.containsKey(clientKey)) {
- return;
- }
- mRcClientMap.put(clientKey, rcClient);
- }
IAudioService service = getService();
try {
- service.registerRemoteControlClient(eventReceiver, mRcClientDispatcher, clientKey,
+ service.registerRemoteControlClient(rcClient.getRcEventReceiver(), /* eventReceiver */
+ rcClient.getIRemoteControlClient(), /* rcClient */
+ rcClient.toString(), /* clientName */
// used to match media button event receiver and audio focus
- mContext.getPackageName());
+ mContext.getPackageName()); /* packageName */
} catch (RemoteException e) {
Log.e(TAG, "Dead object in registerRemoteControlClient"+e);
- synchronized(mRcClientMap) {
- mRcClientMap.remove(clientKey);
- }
}
}
/**
* @hide
+ * CANDIDATE FOR SDK
* Unregisters the remote control client that was providing information to display on the
* remotes.
- * @param eventReceiver identifier of a {@link android.content.BroadcastReceiver}
- * that receives the media button intent, and associated with the remote control
- * client.
* @param rcClient the remote control client to unregister
- * @see #registerRemoteControlClient(ComponentName, RemoteControlClient)
+ * @see #registerRemoteControlClient(RemoteControlClient)
*/
- public void unregisterRemoteControlClient(ComponentName eventReceiver,
- RemoteControlClient rcClient) {
- if ((eventReceiver == null) || (rcClient == null)) {
+ public void unregisterRemoteControlClient(RemoteControlClient rcClient) {
+ if ((rcClient == null) || (rcClient.getRcEventReceiver() == null)) {
return;
}
IAudioService service = getService();
try {
- // remove locally
- boolean unregister = true;
- synchronized(mRcClientMap) {
- if (mRcClientMap.remove(getIdForRcClient(rcClient)) == null) {
- unregister = false;
- }
- }
- if (unregister) {
- // unregistering a RemoteControlClient is equivalent to setting it to null
- service.registerRemoteControlClient(eventReceiver, null, null,
- mContext.getPackageName());
- }
+ service.unregisterRemoteControlClient(rcClient.getRcEventReceiver(), /* eventReceiver */
+ rcClient.getIRemoteControlClient()); /* rcClient */
} catch (RemoteException e) {
Log.e(TAG, "Dead object in unregisterRemoteControlClient"+e);
}
@@ -1852,26 +1763,62 @@
/**
* @hide
- * Returns the current remote control client.
- * @param rcClientId the generation counter that matches the extra
- * {@link AudioManager#EXTRA_REMOTE_CONTROL_CLIENT_GENERATION} in the
- * {@link AudioManager#REMOTE_CONTROL_CLIENT_CHANGED} event
- * @return the current RemoteControlClient from which information to display on the remote
- * control can be retrieved, or null if rcClientId doesn't match the current generation
- * counter.
+ * Registers a remote control display that will be sent information by remote control clients.
+ * @param rcd
*/
- public IRemoteControlClientDispatcher getRemoteControlClientDispatcher(int rcClientId) {
+ public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) {
+ if (rcd == null) {
+ return;
+ }
IAudioService service = getService();
try {
- return service.getRemoteControlClientDispatcher(rcClientId);
+ service.registerRemoteControlDisplay(rcd);
} catch (RemoteException e) {
- Log.e(TAG, "Dead object in getRemoteControlClient "+e);
- return null;
+ Log.e(TAG, "Dead object in registerRemoteControlDisplay " + e);
}
}
/**
* @hide
+ * Unregisters a remote control display that was sent information by remote control clients.
+ * @param rcd
+ */
+ public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
+ if (rcd == null) {
+ return;
+ }
+ IAudioService service = getService();
+ try {
+ service.unregisterRemoteControlDisplay(rcd);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in unregisterRemoteControlDisplay " + e);
+ }
+ }
+
+ /**
+ * @hide
+ * Sets the artwork size a remote control display expects when receiving bitmaps.
+ * @param rcd
+ * @param w the maximum width of the expected bitmap. Negative values indicate it is
+ * useless to send artwork.
+ * @param h the maximum height of the expected bitmap. Negative values indicate it is
+ * useless to send artwork.
+ */
+ public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
+ if (rcd == null) {
+ return;
+ }
+ IAudioService service = getService();
+ try {
+ service.remoteControlDisplayUsesBitmapSize(rcd, w, h);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in remoteControlDisplayUsesBitmapSize " + e);
+ }
+ }
+
+ // FIXME remove because we are not using intents anymore between AudioService and RcDisplay
+ /**
+ * @hide
* Broadcast intent action indicating that the displays on the remote controls
* should be updated because a new remote control client is now active. If there is no
* {@link #EXTRA_REMOTE_CONTROL_CLIENT}, the remote control display should be cleared
@@ -1882,6 +1829,7 @@
public static final String REMOTE_CONTROL_CLIENT_CHANGED =
"android.media.REMOTE_CONTROL_CLIENT_CHANGED";
+ // FIXME remove because we are not using intents anymore between AudioService and RcDisplay
/**
* @hide
* The IRemoteControlClientDispatcher monotonically increasing generation counter.
@@ -1891,6 +1839,7 @@
public static final String EXTRA_REMOTE_CONTROL_CLIENT_GENERATION =
"android.media.EXTRA_REMOTE_CONTROL_CLIENT_GENERATION";
+ // FIXME remove because we are not using intents anymore between AudioService and RcDisplay
/**
* @hide
* The name of the RemoteControlClient.
@@ -1902,6 +1851,7 @@
public static final String EXTRA_REMOTE_CONTROL_CLIENT_NAME =
"android.media.EXTRA_REMOTE_CONTROL_CLIENT_NAME";
+ // FIXME remove because we are not using intents anymore between AudioService and RcDisplay
/**
* @hide
* The media button event receiver associated with the RemoteControlClient.
@@ -1913,6 +1863,7 @@
public static final String EXTRA_REMOTE_CONTROL_EVENT_RECEIVER =
"android.media.EXTRA_REMOTE_CONTROL_EVENT_RECEIVER";
+ // FIXME remove because we are not using intents anymore between AudioService and RcDisplay
/**
* @hide
* The flags describing what information has changed in the current remote control client.
@@ -1923,33 +1874,6 @@
"android.media.EXTRA_REMOTE_CONTROL_CLIENT_INFO_CHANGED";
/**
- * @hide
- * Notifies the users of the associated remote control client that the information to display
- * has changed.
- @param eventReceiver identifier of a {@link android.content.BroadcastReceiver}
- * that will receive the media button intent, and associated with the remote control
- * client. This method has no effect if
- * {@link #registerMediaButtonEventReceiver(ComponentName)} hasn't been called
- * with the same eventReceiver, or if
- * {@link #unregisterMediaButtonEventReceiver(ComponentName)} has been called.
- * @param infoFlag the type of information that has changed since this method was last called,
- * or the event receiver was registered. Use one or multiple of the following flags to
- * describe what changed:
- * {@link RemoteControlClient#FLAG_INFORMATION_CHANGED_METADATA},
- * {@link RemoteControlClient#FLAG_INFORMATION_CHANGED_KEY_MEDIA},
- * {@link RemoteControlClient#FLAG_INFORMATION_CHANGED_PLAYSTATE},
- * {@link RemoteControlClient#FLAG_INFORMATION_CHANGED_ALBUM_ART}.
- */
- public void notifyRemoteControlInformationChanged(ComponentName eventReceiver, int infoFlag) {
- IAudioService service = getService();
- try {
- service.notifyRemoteControlInformationChanged(eventReceiver, infoFlag);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in refreshRemoteControlDisplay"+e);
- }
- }
-
- /**
* @hide
* Reload audio settings. This method is called by Settings backup
* agent when audio settings are restored and causes the AudioService
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 85c7dba..acc2b23 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -34,7 +34,6 @@
import android.database.ContentObserver;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
-import android.media.IRemoteControlClientDispatcher;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
@@ -1557,17 +1556,14 @@
int newRingerMode = mRingerMode;
if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
- if ((direction == AudioManager.ADJUST_LOWER) && ((oldIndex + 5) / 10 <= 1)) {
- // enter silent mode if current index is the last audible one and not repeating a
- // volume key down
- if (mPrevVolDirection != AudioManager.ADJUST_LOWER) {
- // "silent mode", but which one?
- newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1
- ? AudioManager.RINGER_MODE_VIBRATE
- : AudioManager.RINGER_MODE_SILENT;
- } else {
- adjustVolumeIndex = false;
- }
+ // audible mode, at the bottom of the scale
+ if ((direction == AudioManager.ADJUST_LOWER &&
+ mPrevVolDirection != AudioManager.ADJUST_LOWER) &&
+ ((oldIndex + 5) / 10 == 0)) {
+ // "silent mode", but which one?
+ newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1
+ ? AudioManager.RINGER_MODE_VIBRATE
+ : AudioManager.RINGER_MODE_SILENT;
}
} else {
if (direction == AudioManager.ADJUST_RAISE) {
@@ -2170,45 +2166,12 @@
break;
case MSG_RCDISPLAY_CLEAR:
- // TODO remove log before release
- Log.i(TAG, "Clear remote control display");
- Intent clearIntent = new Intent(AudioManager.REMOTE_CONTROL_CLIENT_CHANGED);
- // no extra means no IRemoteControlClientDispatcher, which is a request to clear
- clearIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mContext.sendBroadcast(clearIntent);
+ onRcDisplayClear();
break;
case MSG_RCDISPLAY_UPDATE:
- synchronized(mCurrentRcLock) {
- // msg.obj is guaranteed to be non null
- RemoteControlStackEntry rcse = (RemoteControlStackEntry)msg.obj;
- if ((mCurrentRcClient == null) ||
- (!mCurrentRcClient.equals(rcse.mRcClient))) {
- // the remote control display owner has changed between the
- // the message to update the display was sent, and the time it
- // gets to be processed (now)
- } else {
- mCurrentRcClientGen++;
- // TODO remove log before release
- Log.i(TAG, "Display/update remote control ");
- Intent rcClientIntent = new Intent(
- AudioManager.REMOTE_CONTROL_CLIENT_CHANGED);
- rcClientIntent.putExtra(
- AudioManager.EXTRA_REMOTE_CONTROL_CLIENT_GENERATION,
- mCurrentRcClientGen);
- rcClientIntent.putExtra(
- AudioManager.EXTRA_REMOTE_CONTROL_CLIENT_INFO_CHANGED,
- msg.arg1);
- rcClientIntent.putExtra(
- AudioManager.EXTRA_REMOTE_CONTROL_EVENT_RECEIVER,
- rcse.mReceiverComponent.flattenToString());
- rcClientIntent.putExtra(
- AudioManager.EXTRA_REMOTE_CONTROL_CLIENT_NAME,
- rcse.mRcClientName);
- rcClientIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mContext.sendBroadcast(rcClientIntent);
- }
- }
+ // msg.obj is guaranteed to be non null
+ onRcDisplayUpdate( (RemoteControlStackEntry) msg.obj, msg.arg1);
break;
case MSG_BT_HEADSET_CNCT_FAILED:
@@ -2896,18 +2859,18 @@
private final Object mCurrentRcLock = new Object();
/**
- * The one remote control client to be polled for display information.
+ * The one remote control client which will receive a request for display information.
* This object may be null.
* Access protected by mCurrentRcLock.
*/
- private IRemoteControlClientDispatcher mCurrentRcClient = null;
+ private IRemoteControlClient mCurrentRcClient = null;
private final static int RC_INFO_NONE = 0;
private final static int RC_INFO_ALL =
- RemoteControlClient.FLAG_INFORMATION_CHANGED_ALBUM_ART |
- RemoteControlClient.FLAG_INFORMATION_CHANGED_KEY_MEDIA |
- RemoteControlClient.FLAG_INFORMATION_CHANGED_METADATA |
- RemoteControlClient.FLAG_INFORMATION_CHANGED_PLAYSTATE;
+ RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART |
+ RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA |
+ RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA |
+ RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE;
/**
* A monotonically increasing generation counter for mCurrentRcClient.
@@ -2917,25 +2880,6 @@
private int mCurrentRcClientGen = 0;
/**
- * Returns the current remote control client.
- * @param rcClientId the counter value that matches the extra
- * {@link AudioManager#EXTRA_REMOTE_CONTROL_CLIENT_GENERATION} in the
- * {@link AudioManager#REMOTE_CONTROL_CLIENT_CHANGED} event
- * @return the current IRemoteControlClientDispatcher from which information to display on the
- * remote control can be retrieved, or null if rcClientId doesn't match the current
- * generation counter.
- */
- public IRemoteControlClientDispatcher getRemoteControlClientDispatcher(int rcClientId) {
- synchronized(mCurrentRcLock) {
- if (rcClientId == mCurrentRcClientGen) {
- return mCurrentRcClient;
- } else {
- return null;
- }
- }
- }
-
- /**
* Inner class to monitor remote control client deaths, and remove the client for the
* remote control stack if necessary.
*/
@@ -2968,7 +2912,7 @@
public int mCallingUid;
/** provides access to the information to display on the remote control */
- public IRemoteControlClientDispatcher mRcClient;
+ public IRemoteControlClient mRcClient;
public RcClientDeathHandler mRcClientDeathHandler;
public RemoteControlStackEntry(ComponentName r) {
@@ -3122,6 +3066,103 @@
return false;
}
+ //==========================================================================================
+ // Remote control display / client
+ //==========================================================================================
+ /**
+ * Update the remote control displays with the new "focused" client generation
+ */
+ private void setNewRcClientGenerationOnDisplays_syncRcStack(int newClientGeneration) {
+ // NOTE: Only one IRemoteControlDisplay supported in this implementation
+ if (mRcDisplay != null) {
+ try {
+ mRcDisplay.setCurrentClientGenerationId(newClientGeneration);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead display in onRcDisplayUpdate() "+e);
+ // if we had a display before, stop monitoring its death
+ rcDisplay_stopDeathMonitor_syncRcStack();
+ mRcDisplay = null;
+ }
+ }
+ }
+
+ /**
+ * Update the remote control clients with the new "focused" client generation
+ */
+ private void setNewRcClientGenerationOnClients_syncRcStack(int newClientGeneration) {
+ Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+ while(stackIterator.hasNext()) {
+ RemoteControlStackEntry se = stackIterator.next();
+ if ((se != null) && (se.mRcClient != null)) {
+ try {
+ se.mRcClient.setCurrentClientGenerationId(newClientGeneration);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Dead client in onRcDisplayUpdate()"+e);
+ stackIterator.remove();
+ se.unlinkToRcClientDeath();
+ }
+ }
+ }
+ }
+
+ /**
+ * Update the displays and clients with the new "focused" client generation
+ */
+ private void setNewRcClientGeneration(int newClientGeneration) {
+ synchronized(mRCStack) {
+ // send the new valid client generation ID to all displays
+ setNewRcClientGenerationOnDisplays_syncRcStack(newClientGeneration);
+ // send the new valid client generation ID to all clients
+ setNewRcClientGenerationOnClients_syncRcStack(newClientGeneration);
+ }
+ }
+
+ /**
+ * Called when processing MSG_RCDISPLAY_CLEAR event
+ */
+ private void onRcDisplayClear() {
+ // TODO remove log before release
+ Log.i(TAG, "Clear remote control display");
+
+ synchronized(mCurrentRcLock) {
+ mCurrentRcClientGen++;
+
+ // synchronously update the displays and clients with the new client generation
+ setNewRcClientGeneration(mCurrentRcClientGen);
+ }
+ }
+
+ /**
+ * Called when processing MSG_RCDISPLAY_UPDATE event
+ */
+ private void onRcDisplayUpdate(RemoteControlStackEntry rcse, int flags /* USED ?*/) {
+ synchronized(mCurrentRcLock) {
+ if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(rcse.mRcClient))) {
+ // TODO remove log before release
+ Log.i(TAG, "Display/update remote control ");
+
+ mCurrentRcClientGen++;
+
+ // synchronously update the displays and clients with the new client generation
+ setNewRcClientGeneration(mCurrentRcClientGen);
+
+ // ask the current client that it needs to send info
+ try {
+ mCurrentRcClient.onInformationRequested(mCurrentRcClientGen,
+ flags, mArtworkExpectedWidth, mArtworkExpectedHeight);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Current valid remote client is dead: "+e);
+ mCurrentRcClient = null;
+ }
+ } else {
+ // the remote control display owner has changed between the
+ // the message to update the display was sent, and the time it
+ // gets to be processed (now)
+ }
+ }
+ }
+
+
/**
* Helper function:
* Called synchronized on mRCStack
@@ -3130,6 +3171,7 @@
synchronized(mCurrentRcLock) {
mCurrentRcClient = null;
}
+ // will cause onRcDisplayClear() to be called in AudioService's handler thread
mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) );
}
@@ -3155,6 +3197,7 @@
}
mCurrentRcClient = rcse.mRcClient;
}
+ // will cause onRcDisplayUpdate() to be called in AudioService's handler thread
mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_UPDATE,
infoFlagsAboutToBeUsed /* arg1 */, 0, rcse /* obj, != null */) );
}
@@ -3223,7 +3266,7 @@
/** see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...) */
public void registerRemoteControlClient(ComponentName eventReceiver,
- IRemoteControlClientDispatcher rcClient, String clientName, String callingPackageName) {
+ IRemoteControlClient rcClient, String clientName, String callingPackageName) {
synchronized(mAudioFocusLock) {
synchronized(mRCStack) {
// store the new display information
@@ -3238,6 +3281,14 @@
}
// save the new remote control client
rcse.mRcClient = rcClient;
+ if (mRcDisplay != null) {
+ try {
+ rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error connecting remote control display to client: "+e);
+ e.printStackTrace();
+ }
+ }
rcse.mCallingPackageName = callingPackageName;
rcse.mRcClientName = clientName;
rcse.mCallingUid = Binder.getCallingUid();
@@ -3269,18 +3320,121 @@
}
}
- /** see AudioManager.notifyRemoteControlInformationChanged(ComponentName er, int infoFlag) */
- public void notifyRemoteControlInformationChanged(ComponentName eventReceiver, int infoFlag) {
- synchronized(mAudioFocusLock) {
+ /** see AudioManager.unregisterRemoteControlClient(ComponentName eventReceiver, ...) */
+ public void unregisterRemoteControlClient(ComponentName eventReceiver,
+ IRemoteControlClient rcClient) {
+ //FIXME implement
+ }
+
+ /**
+ * The remote control displays.
+ * Access synchronized on mRCStack
+ * NOTE: Only one IRemoteControlDisplay supported in this implementation
+ */
+ private IRemoteControlDisplay mRcDisplay;
+ private RcDisplayDeathHandler mRcDisplayDeathHandler;
+ private int mArtworkExpectedWidth = -1;
+ private int mArtworkExpectedHeight = -1;
+ /**
+ * Inner class to monitor remote control display deaths, and unregister them from the list
+ * of displays if necessary.
+ */
+ private class RcDisplayDeathHandler implements IBinder.DeathRecipient {
+ public void binderDied() {
synchronized(mRCStack) {
- // only refresh if the eventReceiver is at the top of the stack
- if (isCurrentRcController(eventReceiver)) {
- checkUpdateRemoteControlDisplay(infoFlag);
+ Log.w(TAG, " RemoteControl: display died");
+ mRcDisplay = null;
+ }
+ }
+
+ }
+
+ private void rcDisplay_stopDeathMonitor_syncRcStack() {
+ if (mRcDisplay != null) {
+ // we had a display before, stop monitoring its death
+ IBinder b = mRcDisplay.asBinder();
+ try {
+ b.unlinkToDeath(mRcDisplayDeathHandler, 0);
+ } catch (java.util.NoSuchElementException e) {
+ // being conservative here
+ Log.e(TAG, "Error while trying to unlink display death handler " + e);
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private void rcDisplay_startDeathMonitor_syncRcStack() {
+ if (mRcDisplay != null) {
+ // new non-null display, monitor its death
+ IBinder b = mRcDisplay.asBinder();
+ mRcDisplayDeathHandler = new RcDisplayDeathHandler();
+ try {
+ b.linkToDeath(mRcDisplayDeathHandler, 0);
+ } catch (RemoteException e) {
+ // remote control display is DOA, disqualify it
+ Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + b);
+ mRcDisplay = null;
+ }
+ }
+ }
+
+ public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) {
+ synchronized(mRCStack) {
+ if (mRcDisplay == rcd) {
+ return;
+ }
+ // if we had a display before, stop monitoring its death
+ rcDisplay_stopDeathMonitor_syncRcStack();
+ mRcDisplay = rcd;
+ // new display, start monitoring its death
+ rcDisplay_startDeathMonitor_syncRcStack();
+
+ // let all the remote control clients there is a new display
+ Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+ while(stackIterator.hasNext()) {
+ RemoteControlStackEntry rcse = stackIterator.next();
+ if(rcse.mRcClient != null) {
+ try {
+ rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error connecting remote control display to client: " + e);
+ e.printStackTrace();
+ }
}
}
}
}
+ public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
+ synchronized(mRCStack) {
+ // if we had a display before, stop monitoring its death
+ rcDisplay_stopDeathMonitor_syncRcStack();
+ mRcDisplay = null;
+
+ // disconnect this remote control display from all the clients
+ Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+ while(stackIterator.hasNext()) {
+ RemoteControlStackEntry rcse = stackIterator.next();
+ if(rcse.mRcClient != null) {
+ try {
+ rcse.mRcClient.unplugRemoteControlDisplay(rcd);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error disconnecting remote control display to client: " + e);
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ }
+
+ public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
+ synchronized(mRCStack) {
+ // NOTE: Only one IRemoteControlDisplay supported in this implementation
+ mArtworkExpectedWidth = w;
+ mArtworkExpectedHeight = h;
+ }
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
// TODO probably a lot more to do here than just the audio focus and remote control stacks
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 7f9ced9..7bf9814 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -18,7 +18,8 @@
import android.content.ComponentName;
import android.media.IAudioFocusDispatcher;
-import android.media.IRemoteControlClientDispatcher;
+import android.media.IRemoteControlClient;
+import android.media.IRemoteControlDisplay;
/**
* {@hide}
@@ -88,13 +89,14 @@
void unregisterMediaButtonEventReceiver(in ComponentName eventReceiver);
- void registerRemoteControlClient(in ComponentName eventReceiver,
- in IRemoteControlClientDispatcher rcClient, in String clientName,
- in String callingPackageName);
+ oneway void registerRemoteControlClient(in ComponentName eventReceiver,
+ in IRemoteControlClient rcClient, in String clientName, in String callingPackageName);
+ oneway void unregisterRemoteControlClient(in ComponentName eventReceiver,
+ in IRemoteControlClient rcClient);
- IRemoteControlClientDispatcher getRemoteControlClientDispatcher(in int rcClientId);
-
- void notifyRemoteControlInformationChanged(in ComponentName eventReceiver, int infoFlag);
+ oneway void registerRemoteControlDisplay(in IRemoteControlDisplay rcd);
+ oneway void unregisterRemoteControlDisplay(in IRemoteControlDisplay rcd);
+ oneway void remoteControlDisplayUsesBitmapSize(in IRemoteControlDisplay rcd, int w, int h);
void startBluetoothSco(IBinder cb);
diff --git a/media/java/android/media/IRemoteControlClient.aidl b/media/java/android/media/IRemoteControlClient.aidl
new file mode 100644
index 0000000..0fbba20
--- /dev/null
+++ b/media/java/android/media/IRemoteControlClient.aidl
@@ -0,0 +1,51 @@
+/* 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.media;
+
+import android.graphics.Bitmap;
+import android.media.IRemoteControlDisplay;
+
+/**
+ * @hide
+ * Interface registered by AudioManager to notify a source of remote control information
+ * that information is requested to be displayed on the remote control (through
+ * IRemoteControlDisplay).
+ * {@see AudioManager#registerRemoteControlClient(RemoteControlClient)}.
+ */
+oneway interface IRemoteControlClient
+{
+ /**
+ * Notifies a remote control client that information for the given generation ID is
+ * requested. If the flags contains
+ * {@link RemoteControlClient#FLAG_INFORMATION_REQUESTED_ALBUM_ART} then the width and height
+ * parameters are valid.
+ * @param generationId
+ * @param infoFlags
+ * @param artWidth if > 0, artHeight must be > 0 too.
+ * @param artHeight
+ * FIXME: is infoFlags required? since the RCC pushes info, this might always be called
+ * with RC_INFO_ALL
+ */
+ void onInformationRequested(int generationId, int infoFlags, int artWidth, int artHeight);
+
+ /**
+ * Sets the generation counter of the current client that is displayed on the remote control.
+ */
+ void setCurrentClientGenerationId(int clientGeneration);
+
+ void plugRemoteControlDisplay(IRemoteControlDisplay rcd);
+ void unplugRemoteControlDisplay(IRemoteControlDisplay rcd);
+}
\ No newline at end of file
diff --git a/media/java/android/media/IRemoteControlClientDispatcher.aidl b/media/java/android/media/IRemoteControlClientDispatcher.aidl
deleted file mode 100644
index 98142cc..0000000
--- a/media/java/android/media/IRemoteControlClientDispatcher.aidl
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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.media;
-
-import android.graphics.Bitmap;
-
-/**
- * @hide
- * Interface registered by AudioManager to dispatch remote control information requests
- * to the RemoteControlClient implementation. This is used by AudioService.
- * {@see AudioManager#registerRemoteControlClient(ComponentName, RemoteControlClient)}.
- */
-interface IRemoteControlClientDispatcher
-{
- /**
- * Called by a remote control to retrieve a String of information to display.
- * @param field the identifier for a metadata field to retrieve. Valid values are
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUM},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUMARTIST},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ARTIST},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_AUTHOR},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_CD_TRACK_NUMBER},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPILATION},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPOSER},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DATE},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_GENRE},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_WRITER},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}.
- * @return null if the requested field is not supported, or the String matching the
- * metadata field.
- */
- String getMetadataStringForClient(String clientName, int field);
-
- /**
- * Called by a remote control to retrieve the current playback state.
- * @return one of the following values:
- * {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_STOPPED},
- * {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_PAUSED},
- * {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_PLAYING},
- * {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_FAST_FORWARDING},
- * {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_REWINDING},
- * {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_SKIPPING_FORWARDS},
- * {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_SKIPPING_BACKWARDS},
- * {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_BUFFERING},
- * {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_ERROR}.
- */
- int getPlaybackStateForClient(String clientName);
-
- /**
- * Called by a remote control to retrieve the flags for the media transport control buttons
- * that this client supports.
- * @see {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_PREVIOUS},
- * {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_REWIND},
- * {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_PLAY},
- * {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_PLAY_PAUSE},
- * {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_PAUSE},
- * {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_STOP},
- * {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_FAST_FORWARD},
- * {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_NEXT}
- */
- int getTransportControlFlagsForClient(String clientName);
-
- /**
- * Called by a remote control to retrieve the album art picture at the requested size.
- * Note that returning a bitmap smaller than the maximum requested dimension is accepted
- * and it will be scaled as needed, but exceeding the maximum dimensions may produce
- * unspecified results, such as the image being cropped or simply not being displayed.
- * @param maxWidth the maximum width of the requested bitmap expressed in pixels.
- * @param maxHeight the maximum height of the requested bitmap expressed in pixels.
- * @return the bitmap for the album art, or null if there isn't any.
- * @see android.graphics.Bitmap
- */
- Bitmap getAlbumArtForClient(String clientName, int maxWidth, int maxHeight);
-}
diff --git a/media/java/android/media/IRemoteControlDisplay.aidl b/media/java/android/media/IRemoteControlDisplay.aidl
new file mode 100644
index 0000000..d000906
--- /dev/null
+++ b/media/java/android/media/IRemoteControlDisplay.aidl
@@ -0,0 +1,47 @@
+/*
+ * 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.media;
+
+import android.graphics.Bitmap;
+import android.os.Bundle;
+
+/**
+ * @hide
+ * Interface registered through AudioManager of an object that displays information
+ * received from a remote control client.
+ * {@see AudioManager#registerRemoteControlDisplay(IRemoteControlDisplay)}.
+ */
+oneway interface IRemoteControlDisplay
+{
+ /**
+ * Sets the generation counter of the current client that is displayed on the remote control.
+ */
+ void setCurrentClientGenerationId(int clientGeneration);
+
+ void setPlaybackState(int generationId, int state);
+
+ void setMetadata(int generationId, in Bundle metadata);
+
+ void setTransportControlFlags(int generationId, int transportControlFlags);
+
+ void setArtwork(int generationId, in Bitmap artwork);
+
+ /**
+ * To combine metadata text and artwork in one binder call
+ */
+ void setAllMetadata(int generationId, in Bundle metadata, in Bitmap artwork);
+}
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index c384636..e6331ce 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -17,69 +17,84 @@
package android.media;
import android.content.ComponentName;
+import android.content.SharedPreferences.Editor;
import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
/**
* @hide
- * Interface for an object that exposes information meant to be consumed by remote controls
+ * CANDIDATE FOR SDK
+ * RemoteControlClient enables exposing information meant to be consumed by remote controls
* capable of displaying metadata, album art and media transport control buttons.
- * Such a remote control client object is associated with a media button event receiver
+ * A remote control client object is associated with a media button event receiver
* when registered through
- * {@link AudioManager#registerRemoteControlClient(ComponentName, RemoteControlClient)}.
+ * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}.
*/
-public interface RemoteControlClient
+public class RemoteControlClient
{
+ private final static String TAG = "RemoteControlClient";
+
/**
* Playback state of a RemoteControlClient which is stopped.
*
- * @see android.media.RemoteControlClient#getPlaybackState()
+ * @see #setPlaybackState(int)
*/
public final static int PLAYSTATE_STOPPED = 1;
/**
* Playback state of a RemoteControlClient which is paused.
*
- * @see android.media.RemoteControlClient#getPlaybackState()
+ * @see #setPlaybackState(int)
*/
public final static int PLAYSTATE_PAUSED = 2;
/**
* Playback state of a RemoteControlClient which is playing media.
*
- * @see android.media.RemoteControlClient#getPlaybackState()
+ * @see #setPlaybackState(int)
*/
public final static int PLAYSTATE_PLAYING = 3;
/**
* Playback state of a RemoteControlClient which is fast forwarding in the media
* it is currently playing.
*
- * @see android.media.RemoteControlClient#getPlaybackState()
+ * @see #setPlaybackState(int)
*/
public final static int PLAYSTATE_FAST_FORWARDING = 4;
/**
* Playback state of a RemoteControlClient which is fast rewinding in the media
* it is currently playing.
*
- * @see android.media.RemoteControlClient#getPlaybackState()
+ * @see #setPlaybackState(int)
*/
public final static int PLAYSTATE_REWINDING = 5;
/**
* Playback state of a RemoteControlClient which is skipping to the next
* logical chapter (such as a song in a playlist) in the media it is currently playing.
*
- * @see android.media.RemoteControlClient#getPlaybackState()
+ * @see #setPlaybackState(int)
*/
public final static int PLAYSTATE_SKIPPING_FORWARDS = 6;
/**
* Playback state of a RemoteControlClient which is skipping back to the previous
* logical chapter (such as a song in a playlist) in the media it is currently playing.
*
- * @see android.media.RemoteControlClient#getPlaybackState()
+ * @see #setPlaybackState(int)
*/
public final static int PLAYSTATE_SKIPPING_BACKWARDS = 7;
/**
* Playback state of a RemoteControlClient which is buffering data to play before it can
* start or resume playback.
*
- * @see android.media.RemoteControlClient#getPlaybackState()
+ * @see #setPlaybackState(int)
*/
public final static int PLAYSTATE_BUFFERING = 8;
/**
@@ -88,121 +103,286 @@
* connectivity when attempting to stream data from a server, or expired user credentials
* when trying to play subscription-based content.
*
- * @see android.media.RemoteControlClient#getPlaybackState()
+ * @see #setPlaybackState(int)
*/
public final static int PLAYSTATE_ERROR = 9;
+ /**
+ * @hide
+ * The value of a playback state when none has been declared
+ */
+ public final static int PLAYSTATE_NONE = 0;
/**
* Flag indicating a RemoteControlClient makes use of the "previous" media key.
*
- * @see android.media.RemoteControlClient#getTransportControlFlags()
+ * @see #setTransportControlFlags(int)
* @see android.view.KeyEvent#KEYCODE_MEDIA_PREVIOUS
*/
public final static int FLAG_KEY_MEDIA_PREVIOUS = 1 << 0;
/**
* Flag indicating a RemoteControlClient makes use of the "rewing" media key.
*
- * @see android.media.RemoteControlClient#getTransportControlFlags()
+ * @see #setTransportControlFlags(int)
* @see android.view.KeyEvent#KEYCODE_MEDIA_REWIND
*/
public final static int FLAG_KEY_MEDIA_REWIND = 1 << 1;
/**
* Flag indicating a RemoteControlClient makes use of the "play" media key.
*
- * @see android.media.RemoteControlClient#getTransportControlFlags()
+ * @see #setTransportControlFlags(int)
* @see android.view.KeyEvent#KEYCODE_MEDIA_PLAY
*/
public final static int FLAG_KEY_MEDIA_PLAY = 1 << 2;
/**
* Flag indicating a RemoteControlClient makes use of the "play/pause" media key.
*
- * @see android.media.RemoteControlClient#getTransportControlFlags()
+ * @see #setTransportControlFlags(int)
* @see android.view.KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE
*/
public final static int FLAG_KEY_MEDIA_PLAY_PAUSE = 1 << 3;
/**
* Flag indicating a RemoteControlClient makes use of the "pause" media key.
*
- * @see android.media.RemoteControlClient#getTransportControlFlags()
+ * @see #setTransportControlFlags(int)
* @see android.view.KeyEvent#KEYCODE_MEDIA_PAUSE
*/
public final static int FLAG_KEY_MEDIA_PAUSE = 1 << 4;
/**
* Flag indicating a RemoteControlClient makes use of the "stop" media key.
*
- * @see android.media.RemoteControlClient#getTransportControlFlags()
+ * @see #setTransportControlFlags(int)
* @see android.view.KeyEvent#KEYCODE_MEDIA_STOP
*/
public final static int FLAG_KEY_MEDIA_STOP = 1 << 5;
/**
* Flag indicating a RemoteControlClient makes use of the "fast forward" media key.
*
- * @see android.media.RemoteControlClient#getTransportControlFlags()
+ * @see #setTransportControlFlags(int)
* @see android.view.KeyEvent#KEYCODE_MEDIA_FAST_FORWARD
*/
public final static int FLAG_KEY_MEDIA_FAST_FORWARD = 1 << 6;
/**
* Flag indicating a RemoteControlClient makes use of the "next" media key.
*
- * @see android.media.RemoteControlClient#getTransportControlFlags()
+ * @see #setTransportControlFlags(int)
* @see android.view.KeyEvent#KEYCODE_MEDIA_NEXT
*/
public final static int FLAG_KEY_MEDIA_NEXT = 1 << 7;
/**
- * Flag used to signal that the metadata exposed by the RemoteControlClient has changed.
- *
- * @see #notifyRemoteControlInformationChanged(ComponentName, int)
+ * @hide
+ * The flags for when no media keys are declared supported
*/
- public final static int FLAG_INFORMATION_CHANGED_METADATA = 1 << 0;
+ public final static int FLAGS_KEY_MEDIA_NONE = 0;
+
/**
+ * @hide
+ * Flag used to signal some type of metadata exposed by the RemoteControlClient is requested.
+ */
+ public final static int FLAG_INFORMATION_REQUEST_METADATA = 1 << 0;
+ /**
+ * @hide
+ * FIXME doc not valid
* Flag used to signal that the transport control buttons supported by the
* RemoteControlClient have changed.
* This can for instance happen when playback is at the end of a playlist, and the "next"
* operation is not supported anymore.
- *
- * @see #notifyRemoteControlInformationChanged(ComponentName, int)
*/
- public final static int FLAG_INFORMATION_CHANGED_KEY_MEDIA = 1 << 1;
+ public final static int FLAG_INFORMATION_REQUEST_KEY_MEDIA = 1 << 1;
/**
+ * @hide
+ * FIXME doc not valid
* Flag used to signal that the playback state of the RemoteControlClient has changed.
- *
- * @see #notifyRemoteControlInformationChanged(ComponentName, int)
*/
- public final static int FLAG_INFORMATION_CHANGED_PLAYSTATE = 1 << 2;
+ public final static int FLAG_INFORMATION_REQUEST_PLAYSTATE = 1 << 2;
/**
+ * @hide
+ * FIXME doc not valid
* Flag used to signal that the album art for the RemoteControlClient has changed.
- *
- * @see #notifyRemoteControlInformationChanged(ComponentName, int)
*/
- public final static int FLAG_INFORMATION_CHANGED_ALBUM_ART = 1 << 3;
+ public final static int FLAG_INFORMATION_REQUEST_ALBUM_ART = 1 << 3;
/**
- * Called by a remote control to retrieve a String of information to display.
- * @param field the identifier for a metadata field to retrieve. Valid values are
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUM},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUMARTIST},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ARTIST},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_AUTHOR},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_CD_TRACK_NUMBER},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPILATION},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPOSER},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DATE},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_GENRE},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_WRITER},
- * {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}.
- * @return null if the requested field is not supported, or the String matching the
- * metadata field.
+ * Class constructor.
+ * @param mediaButtonEventReceiver the receiver for the media button events.
+ * @see AudioManager#registerMediaButtonEventReceiver(ComponentName)
+ * @see AudioManager#registerRemoteControlClient(RemoteControlClient)
*/
- String getMetadataString(int field);
+ public RemoteControlClient(ComponentName mediaButtonEventReceiver) {
+ mRcEventReceiver = mediaButtonEventReceiver;
+
+ Looper looper;
+ if ((looper = Looper.myLooper()) != null) {
+ mEventHandler = new EventHandler(this, looper);
+ } else if ((looper = Looper.getMainLooper()) != null) {
+ mEventHandler = new EventHandler(this, looper);
+ } else {
+ mEventHandler = null;
+ Log.e(TAG, "RemoteControlClient() couldn't find main application thread");
+ }
+ }
/**
- * Called by a remote control to retrieve the current playback state.
- * @return one of the following values:
+ * Class constructor for a remote control client whose internal event handling
+ * happens on a user-provided Looper.
+ * @param mediaButtonEventReceiver the receiver for the media button events.
+ * @param looper the Looper running the event loop.
+ * @see AudioManager#registerMediaButtonEventReceiver(ComponentName)
+ * @see AudioManager#registerRemoteControlClient(RemoteControlClient)
+ */
+ public RemoteControlClient(ComponentName mediaButtonEventReceiver, Looper looper) {
+ mRcEventReceiver = mediaButtonEventReceiver;
+
+ mEventHandler = new EventHandler(this, looper);
+ }
+
+ /**
+ * Class used to modify metadata in a {@link RemoteControlClient} object.
+ */
+ public class MetadataEditor {
+ protected boolean mMetadataChanged;
+ protected boolean mArtworkChanged;
+ protected Bitmap mEditorArtwork;
+ protected Bundle mEditorMetadata;
+ private boolean mApplied = false;
+
+ // only use RemoteControlClient.editMetadata() to get a MetadataEditor instance
+ private MetadataEditor() { }
+ /**
+ * @hide
+ */
+ public Object clone() throws CloneNotSupportedException {
+ throw new CloneNotSupportedException();
+ }
+
+ /**
+ * Adds textual information to be displayed.
+ * Note that none of the information added after {@link #apply()} has been called,
+ * will be displayed.
+ * @param key the identifier of a the metadata field to set. Valid values are
+ * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUM},
+ * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUMARTIST},
+ * {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE},
+ * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ARTIST},
+ * {@link android.media.MediaMetadataRetriever#METADATA_KEY_AUTHOR},
+ * {@link android.media.MediaMetadataRetriever#METADATA_KEY_CD_TRACK_NUMBER},
+ * {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPILATION},
+ * {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPOSER},
+ * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DATE},
+ * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER},
+ * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION},
+ * {@link android.media.MediaMetadataRetriever#METADATA_KEY_GENRE},
+ * {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE},
+ * {@link android.media.MediaMetadataRetriever#METADATA_KEY_WRITER},
+ * {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}.
+ * @param value the text for the given key, or null to signify there is no valid
+ * information for the field.
+ * @return FIXME description
+ */
+ public synchronized MetadataEditor putString(int key, String value) {
+ if (mApplied) {
+ Log.e(TAG, "Can't edit a previously applied MetadataEditor");
+ return this;
+ }
+ mEditorMetadata.putString(String.valueOf(key), value);
+ mMetadataChanged = true;
+ return this;
+ }
+
+ /**
+ * The metadata key for the content artwork / album art.
+ */
+ public final int METADATA_KEY_ARTWORK = 100;
+
+ /**
+ * Sets the album / artwork picture to be displayed on the remote control.
+ * @param key FIXME description
+ * @param bitmap the bitmap for the artwork, or null if there isn't any.
+ * @return FIXME description
+ * @see android.graphics.Bitmap
+ */
+ public synchronized MetadataEditor putBitmap(int key, Bitmap bitmap) {
+ if (mApplied) {
+ Log.e(TAG, "Can't edit a previously applied MetadataEditor");
+ return this;
+ }
+ if (key != METADATA_KEY_ARTWORK) {
+ return this;
+ }
+ if ((mArtworkExpectedWidth > 0) && (mArtworkExpectedHeight > 0)) {
+ mEditorArtwork = scaleBitmapIfTooBig(bitmap,
+ mArtworkExpectedWidth, mArtworkExpectedHeight);
+ } else {
+ // no valid resize dimensions, store as is
+ mEditorArtwork = bitmap;
+ }
+ mArtworkChanged = true;
+ return this;
+ }
+
+ /**
+ * FIXME description
+ */
+ public synchronized void clear() {
+ if (mApplied) {
+ Log.e(TAG, "Can't clear a previously applied MetadataEditor");
+ return;
+ }
+ mEditorMetadata.clear();
+ mEditorArtwork = null;
+ }
+
+ /**
+ * FIXME description
+ */
+ public synchronized void apply() {
+ if (mApplied) {
+ Log.e(TAG, "Can't apply a previously applied MetadataEditor");
+ return;
+ }
+ synchronized(mCacheLock) {
+ // assign the edited data
+ mMetadata = new Bundle(mEditorMetadata);
+ mArtwork = mEditorArtwork;
+ if (mMetadataChanged & mArtworkChanged) {
+ // send to remote control display if conditions are met
+ sendMetadataWithArtwork_syncCacheLock();
+ } else if (mMetadataChanged) {
+ // send to remote control display if conditions are met
+ sendMetadata_syncCacheLock();
+ } else if (mArtworkChanged) {
+ // send to remote control display if conditions are met
+ sendArtwork_syncCacheLock();
+ }
+ mApplied = true;
+ }
+ }
+ }
+
+ /**
+ * FIXME description
+ * @param startEmpty
+ * @return
+ */
+ public MetadataEditor editMetadata(boolean startEmpty) {
+ MetadataEditor editor = new MetadataEditor();
+ if (startEmpty) {
+ editor.mEditorMetadata = new Bundle();
+ editor.mEditorArtwork = null;
+ editor.mMetadataChanged = true;
+ editor.mArtworkChanged = true;
+ } else {
+ editor.mEditorMetadata = new Bundle(mMetadata);
+ editor.mEditorArtwork = mArtwork;
+ editor.mMetadataChanged = false;
+ editor.mArtworkChanged = false;
+ }
+ return editor;
+ }
+
+ /**
+ * Sets the current playback state.
+ * @param state the current playback state, one of the following values:
* {@link #PLAYSTATE_STOPPED},
* {@link #PLAYSTATE_PAUSED},
* {@link #PLAYSTATE_PLAYING},
@@ -213,12 +393,20 @@
* {@link #PLAYSTATE_BUFFERING},
* {@link #PLAYSTATE_ERROR}.
*/
- int getPlaybackState();
+ public void setPlaybackState(int state) {
+ synchronized(mCacheLock) {
+ // store locally
+ mPlaybackState = state;
+
+ // send to remote control display if conditions are met
+ sendPlaybackState_syncCacheLock();
+ }
+ }
/**
- * Called by a remote control to retrieve the flags for the media transport control buttons
- * that this client supports.
- * @see {@link #FLAG_KEY_MEDIA_PREVIOUS},
+ * Sets the flags for the media transport control buttons that this client supports.
+ * @param a combination of the following flags:
+ * {@link #FLAG_KEY_MEDIA_PREVIOUS},
* {@link #FLAG_KEY_MEDIA_REWIND},
* {@link #FLAG_KEY_MEDIA_PLAY},
* {@link #FLAG_KEY_MEDIA_PLAY_PAUSE},
@@ -227,17 +415,329 @@
* {@link #FLAG_KEY_MEDIA_FAST_FORWARD},
* {@link #FLAG_KEY_MEDIA_NEXT}
*/
- int getTransportControlFlags();
+ public void setTransportControlFlags(int transportControlFlags) {
+ synchronized(mCacheLock) {
+ // store locally
+ mTransportControlFlags = transportControlFlags;
+
+ // send to remote control display if conditions are met
+ sendTransportControlFlags_syncCacheLock();
+ }
+ }
/**
- * Called by a remote control to retrieve the album art picture at the requested size.
- * Note that returning a bitmap smaller than the maximum requested dimension is accepted
- * and it will be scaled as needed, but exceeding the maximum dimensions may produce
- * unspecified results, such as the image being cropped or simply not being displayed.
- * @param maxWidth the maximum width of the requested bitmap expressed in pixels.
- * @param maxHeight the maximum height of the requested bitmap expressed in pixels.
- * @return the bitmap for the album art, or null if there isn't any.
- * @see android.graphics.Bitmap
+ * Lock for all cached data
*/
- Bitmap getAlbumArt(int maxWidth, int maxHeight);
+ private final Object mCacheLock = new Object();
+ /**
+ * Cache for the playback state.
+ * Access synchronized on mCacheLock
+ */
+ private int mPlaybackState = PLAYSTATE_NONE;
+ /**
+ * Cache for the artwork bitmap.
+ * Access synchronized on mCacheLock
+ * Artwork and metadata are not kept in one Bundle because the bitmap sometimes needs to be
+ * accessed to be resized, in which case a copy will be made. This would add overhead in
+ * Bundle operations.
+ */
+ private Bitmap mArtwork;
+ private final int ARTWORK_DEFAULT_SIZE = 256;
+ private final int ARTWORK_INVALID_SIZE = -1;
+ private int mArtworkExpectedWidth = ARTWORK_DEFAULT_SIZE;
+ private int mArtworkExpectedHeight = ARTWORK_DEFAULT_SIZE;
+ /**
+ * Cache for the transport control mask.
+ * Access synchronized on mCacheLock
+ */
+ private int mTransportControlFlags = FLAGS_KEY_MEDIA_NONE;
+ /**
+ * Cache for the metadata strings.
+ * Access synchronized on mCacheLock
+ */
+ private Bundle mMetadata = new Bundle();
+
+ /**
+ * The current remote control client generation ID across the system
+ */
+ private int mCurrentClientGenId = -1;
+ /**
+ * The remote control client generation ID, the last time it was told it was the current RC.
+ * If (mCurrentClientGenId == mInternalClientGenId) is true, it means that this remote control
+ * client is the "focused" one, and that whenever this client's info is updated, it needs to
+ * send it to the known IRemoteControlDisplay interfaces.
+ */
+ private int mInternalClientGenId = -2;
+
+ /**
+ * The media button event receiver associated with this remote control client
+ */
+ private final ComponentName mRcEventReceiver;
+
+ /**
+ * The remote control display to which this client will send information.
+ * NOTE: Only one IRemoteControlDisplay supported in this implementation
+ */
+ private IRemoteControlDisplay mRcDisplay;
+
+ /**
+ * @hide
+ * Accessor to media button event receiver
+ */
+ public ComponentName getRcEventReceiver() {
+ return mRcEventReceiver;
+ }
+ /**
+ * @hide
+ * Accessor to IRemoteControlClient
+ */
+ public IRemoteControlClient getIRemoteControlClient() {
+ return mIRCC;
+ }
+
+ /**
+ * The IRemoteControlClient implementation
+ */
+ private IRemoteControlClient mIRCC = new IRemoteControlClient.Stub() {
+
+ public void onInformationRequested(int clientGeneration, int infoFlags,
+ int artWidth, int artHeight) {
+ // only post messages, we can't block here
+ if (mEventHandler != null) {
+ // signal new client
+ mEventHandler.removeMessages(MSG_NEW_INTERNAL_CLIENT_GEN);
+ mEventHandler.dispatchMessage(
+ mEventHandler.obtainMessage(
+ MSG_NEW_INTERNAL_CLIENT_GEN,
+ artWidth, artHeight,
+ new Integer(clientGeneration)));
+ // send the information
+ mEventHandler.removeMessages(MSG_REQUEST_PLAYBACK_STATE);
+ mEventHandler.removeMessages(MSG_REQUEST_METADATA);
+ mEventHandler.removeMessages(MSG_REQUEST_TRANSPORTCONTROL);
+ mEventHandler.removeMessages(MSG_REQUEST_ARTWORK);
+ mEventHandler.dispatchMessage(
+ mEventHandler.obtainMessage(MSG_REQUEST_PLAYBACK_STATE));
+ mEventHandler.dispatchMessage(
+ mEventHandler.obtainMessage(MSG_REQUEST_TRANSPORTCONTROL));
+ mEventHandler.dispatchMessage(mEventHandler.obtainMessage(MSG_REQUEST_METADATA));
+ mEventHandler.dispatchMessage(mEventHandler.obtainMessage(MSG_REQUEST_ARTWORK));
+ }
+ }
+
+ public void setCurrentClientGenerationId(int clientGeneration) {
+ // only post messages, we can't block here
+ if (mEventHandler != null) {
+ mEventHandler.removeMessages(MSG_NEW_CURRENT_CLIENT_GEN);
+ mEventHandler.dispatchMessage(mEventHandler.obtainMessage(
+ MSG_NEW_CURRENT_CLIENT_GEN, clientGeneration, 0/*ignored*/));
+ }
+ }
+
+ public void plugRemoteControlDisplay(IRemoteControlDisplay rcd) {
+ // only post messages, we can't block here
+ if (mEventHandler != null) {
+ mEventHandler.dispatchMessage(mEventHandler.obtainMessage(
+ MSG_PLUG_DISPLAY, rcd));
+ }
+ }
+
+ public void unplugRemoteControlDisplay(IRemoteControlDisplay rcd) {
+ // only post messages, we can't block here
+ if (mEventHandler != null) {
+ mEventHandler.dispatchMessage(mEventHandler.obtainMessage(
+ MSG_UNPLUG_DISPLAY, rcd));
+ }
+ }
+ };
+
+ private EventHandler mEventHandler;
+ private final static int MSG_REQUEST_PLAYBACK_STATE = 1;
+ private final static int MSG_REQUEST_METADATA = 2;
+ private final static int MSG_REQUEST_TRANSPORTCONTROL = 3;
+ private final static int MSG_REQUEST_ARTWORK = 4;
+ private final static int MSG_NEW_INTERNAL_CLIENT_GEN = 5;
+ private final static int MSG_NEW_CURRENT_CLIENT_GEN = 6;
+ private final static int MSG_PLUG_DISPLAY = 7;
+ private final static int MSG_UNPLUG_DISPLAY = 8;
+
+ private class EventHandler extends Handler {
+ public EventHandler(RemoteControlClient rcc, Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch(msg.what) {
+ case MSG_REQUEST_PLAYBACK_STATE:
+ synchronized (mCacheLock) {
+ sendPlaybackState_syncCacheLock();
+ }
+ break;
+ case MSG_REQUEST_METADATA:
+ synchronized (mCacheLock) {
+ sendMetadata_syncCacheLock();
+ }
+ break;
+ case MSG_REQUEST_TRANSPORTCONTROL:
+ synchronized (mCacheLock) {
+ sendTransportControlFlags_syncCacheLock();
+ }
+ break;
+ case MSG_REQUEST_ARTWORK:
+ synchronized (mCacheLock) {
+ sendArtwork_syncCacheLock();
+ }
+ break;
+ case MSG_NEW_INTERNAL_CLIENT_GEN:
+ onNewInternalClientGen((Integer)msg.obj, msg.arg1, msg.arg2);
+ break;
+ case MSG_NEW_CURRENT_CLIENT_GEN:
+ onNewCurrentClientGen(msg.arg1);
+ break;
+ case MSG_PLUG_DISPLAY:
+ onPlugDisplay((IRemoteControlDisplay)msg.obj);
+ break;
+ case MSG_UNPLUG_DISPLAY:
+ onUnplugDisplay((IRemoteControlDisplay)msg.obj);
+ break;
+ default:
+ Log.e(TAG, "Unknown event " + msg.what + " in RemoteControlClient handler");
+ }
+ }
+ }
+
+ private void detachFromDisplay_syncCacheLock() {
+ mRcDisplay = null;
+ mArtworkExpectedWidth = ARTWORK_INVALID_SIZE;
+ mArtworkExpectedHeight = ARTWORK_INVALID_SIZE;
+ }
+
+ private void sendPlaybackState_syncCacheLock() {
+ if ((mCurrentClientGenId == mInternalClientGenId) && (mRcDisplay != null)) {
+ try {
+ mRcDisplay.setPlaybackState(mInternalClientGenId, mPlaybackState);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in setPlaybackState(), dead display "+e);
+ detachFromDisplay_syncCacheLock();
+ }
+ }
+ }
+
+ private void sendMetadata_syncCacheLock() {
+ if ((mCurrentClientGenId == mInternalClientGenId) && (mRcDisplay != null)) {
+ try {
+ mRcDisplay.setMetadata(mInternalClientGenId, mMetadata);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in sendPlaybackState(), dead display "+e);
+ detachFromDisplay_syncCacheLock();
+ }
+ }
+ }
+
+ private void sendTransportControlFlags_syncCacheLock() {
+ if ((mCurrentClientGenId == mInternalClientGenId) && (mRcDisplay != null)) {
+ try {
+ mRcDisplay.setTransportControlFlags(mInternalClientGenId,
+ mTransportControlFlags);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in sendTransportControlFlags(), dead display "+e);
+ detachFromDisplay_syncCacheLock();
+ }
+ }
+ }
+
+ private void sendArtwork_syncCacheLock() {
+ if ((mCurrentClientGenId == mInternalClientGenId) && (mRcDisplay != null)) {
+ // even though we have already scaled in setArtwork(), when this client needs to
+ // send the bitmap, there might be newer and smaller expected dimensions, so we have
+ // to check again.
+ mArtwork = scaleBitmapIfTooBig(mArtwork, mArtworkExpectedWidth, mArtworkExpectedHeight);
+ try {
+ mRcDisplay.setArtwork(mInternalClientGenId, mArtwork);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in sendArtwork(), dead display "+e);
+ detachFromDisplay_syncCacheLock();
+ }
+ }
+ }
+
+ private void sendMetadataWithArtwork_syncCacheLock() {
+ if ((mCurrentClientGenId == mInternalClientGenId) && (mRcDisplay != null)) {
+ // even though we have already scaled in setArtwork(), when this client needs to
+ // send the bitmap, there might be newer and smaller expected dimensions, so we have
+ // to check again.
+ mArtwork = scaleBitmapIfTooBig(mArtwork, mArtworkExpectedWidth, mArtworkExpectedHeight);
+ try {
+ mRcDisplay.setAllMetadata(mInternalClientGenId, mMetadata, mArtwork);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in setAllMetadata(), dead display "+e);
+ detachFromDisplay_syncCacheLock();
+ }
+ }
+ }
+
+ private void onNewInternalClientGen(Integer clientGeneration, int artWidth, int artHeight) {
+ synchronized (mCacheLock) {
+ // this remote control client is told it is the "focused" one:
+ // it implies that now (mCurrentClientGenId == mInternalClientGenId) is true
+ mInternalClientGenId = clientGeneration.intValue();
+ if (artWidth > 0) {
+ mArtworkExpectedWidth = artWidth;
+ mArtworkExpectedHeight = artHeight;
+ }
+ }
+ }
+
+ private void onNewCurrentClientGen(int clientGeneration) {
+ synchronized (mCacheLock) {
+ mCurrentClientGenId = clientGeneration;
+ }
+ }
+
+ private void onPlugDisplay(IRemoteControlDisplay rcd) {
+ synchronized(mCacheLock) {
+ mRcDisplay = rcd;
+ }
+ }
+
+ private void onUnplugDisplay(IRemoteControlDisplay rcd) {
+ synchronized(mCacheLock) {
+ if ((mRcDisplay != null) && (mRcDisplay.equals(rcd))) {
+ mRcDisplay = null;
+ mArtworkExpectedWidth = ARTWORK_DEFAULT_SIZE;
+ mArtworkExpectedHeight = ARTWORK_DEFAULT_SIZE;
+ }
+ }
+ }
+
+ /**
+ * Scale a bitmap to fit the smallest dimension by uniformly scaling the incoming bitmap.
+ * If the bitmap fits, then do nothing and return the original.
+ *
+ * @param bitmap
+ * @param maxWidth
+ * @param maxHeight
+ * @return
+ */
+
+ private Bitmap scaleBitmapIfTooBig(Bitmap bitmap, int maxWidth, int maxHeight) {
+ final int width = bitmap.getWidth();
+ final int height = bitmap.getHeight();
+ if (width > maxWidth || height > maxHeight) {
+ float scale = Math.min((float) maxWidth / width, (float) maxHeight / height);
+ int newWidth = Math.round(scale * width);
+ int newHeight = Math.round(scale * height);
+ Bitmap outBitmap = Bitmap.createBitmap(newWidth, newHeight, bitmap.getConfig());
+ Canvas canvas = new Canvas(outBitmap);
+ Paint paint = new Paint();
+ paint.setAntiAlias(true);
+ paint.setFilterBitmap(true);
+ canvas.drawBitmap(bitmap, null,
+ new RectF(0, 0, outBitmap.getWidth(), outBitmap.getHeight()), paint);
+ bitmap = outBitmap;
+ }
+ return bitmap;
+
+ }
}
diff --git a/media/java/android/media/videoeditor/MediaImageItem.java b/media/java/android/media/videoeditor/MediaImageItem.java
index 4ca6fad..b2a279a 100755
--- a/media/java/android/media/videoeditor/MediaImageItem.java
+++ b/media/java/android/media/videoeditor/MediaImageItem.java
@@ -626,7 +626,7 @@
if (getGeneratedImageClip() == null) {
final Bitmap thumbnail = scaleImage(mFilename, width, height);
for (int i = 0; i < indices.length; i++) {
- callback.onThumbnail(thumbnail, i);
+ callback.onThumbnail(thumbnail, indices[i]);
}
} else {
if (startMs > endMs) {
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index 92e84c2..09f91f5 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -52,7 +52,10 @@
*post_id3_pos = 0;
}
+ bool resync_from_head = false;
if (*inout_pos == 0) {
+ resync_from_head = true;
+
// Skip an optional ID3 header if syncing at the very beginning
// of the datasource.
@@ -137,22 +140,20 @@
uint32_t header = U32_AT(tmp);
- if (match_header != 0 && (header & kMask) != (match_header & kMask)) {
- ++pos;
- ++tmp;
- --remainingBytes;
- continue;
- }
-
size_t frame_size;
- int sample_rate, num_channels, bitrate;
- if (!GetMPEGAudioFrameSize(
- header, &frame_size,
- &sample_rate, &num_channels, &bitrate)) {
- ++pos;
- ++tmp;
- --remainingBytes;
- continue;
+ if ((match_header != 0 && (header & kMask) != (match_header & kMask))
+ || !GetMPEGAudioFrameSize(header, &frame_size)) {
+ if (resync_from_head) {
+ // This isn't a valid mp3 file because it failed to detect
+ // a header while a valid mp3 file should have a valid
+ // header here.
+ break;
+ } else {
+ ++pos;
+ ++tmp;
+ --remainingBytes;
+ continue;
+ }
}
LOGV("found possible 1st frame at %lld (header = 0x%08x)", pos, header);
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 27dfeab..525ee8b 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -808,7 +808,7 @@
}
if (format.eCompressionFormat == compressionFormat
- && format.eColorFormat == colorFormat) {
+ && format.eColorFormat == colorFormat) {
found = true;
break;
}
@@ -838,6 +838,15 @@
case OMX_COLOR_FormatYUV420Planar:
case OMX_COLOR_FormatYUV420SemiPlanar:
case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar:
+ /*
+ * FIXME: For the Opaque color format, the frame size does not
+ * need to be (w*h*3)/2. It just needs to
+ * be larger than certain minimum buffer size. However,
+ * currently, this opaque foramt has been tested only on
+ * YUV420 formats. If that is changed, then we need to revisit
+ * this part in the future
+ */
+ case OMX_COLOR_FormatAndroidOpaque:
return (width * height * 3) / 2;
default:
@@ -887,7 +896,7 @@
// Make sure that omx component does not overwrite
// the incremented index (bug 2897413).
CHECK_EQ(index, portFormat.nIndex);
- if ((portFormat.eColorFormat == colorFormat)) {
+ if (portFormat.eColorFormat == colorFormat) {
LOGV("Found supported color format: %d", portFormat.eColorFormat);
return OK; // colorFormat is supported!
}
@@ -2923,6 +2932,7 @@
size_t offset = 0;
int32_t n = 0;
+
for (;;) {
MediaBuffer *srcBuffer;
if (mSeekTimeUs >= 0) {
@@ -3021,6 +3031,7 @@
CHECK(info->mMediaBuffer == NULL);
info->mMediaBuffer = srcBuffer;
} else {
+ CHECK(srcBuffer->data() != NULL) ;
memcpy((uint8_t *)info->mData + offset,
(const uint8_t *)srcBuffer->data()
+ srcBuffer->range_offset(),
diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp
index ddfd9ff..c2e6707 100644
--- a/media/libstagefright/SurfaceMediaSource.cpp
+++ b/media/libstagefright/SurfaceMediaSource.cpp
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
// #define LOG_NDEBUG 0
#define LOG_TAG "SurfaceMediaSource"
@@ -47,7 +46,9 @@
mSynchronousMode(true),
mConnectedApi(NO_CONNECTED_API),
mFrameRate(30),
- mStarted(false) {
+ mNumFramesReceived(0),
+ mNumFramesEncoded(0),
+ mStopped(false) {
LOGV("SurfaceMediaSource::SurfaceMediaSource");
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
@@ -55,10 +56,9 @@
SurfaceMediaSource::~SurfaceMediaSource() {
LOGV("SurfaceMediaSource::~SurfaceMediaSource");
- if (mStarted) {
+ if (!mStopped) {
stop();
}
- freeAllBuffers();
}
size_t SurfaceMediaSource::getQueuedCount() const {
@@ -139,12 +139,12 @@
// here we're guaranteed that the client doesn't have dequeued buffers
// and will release all of its buffer references.
- freeAllBuffers();
mBufferCount = bufferCount;
mClientBufferCount = bufferCount;
mCurrentSlot = INVALID_BUFFER_SLOT;
mQueue.clear();
mDequeueCondition.signal();
+ freeAllBuffersLocked();
return OK;
}
@@ -164,7 +164,7 @@
status_t SurfaceMediaSource::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
uint32_t format, uint32_t usage) {
LOGV("dequeueBuffer");
-
+ Mutex::Autolock lock(mMutex);
// Check for the buffer size- the client should just use the
// default width and height, and not try to set those.
@@ -184,10 +184,7 @@
return BAD_VALUE;
}
- Mutex::Autolock lock(mMutex);
-
status_t returnFlags(OK);
-
int found, foundSync;
int dequeuedCount = 0;
bool tryAgain = true;
@@ -218,6 +215,9 @@
LOGV("Waiting for the FIFO to drain");
mDequeueCondition.wait(mMutex);
}
+ if (mStopped) {
+ return NO_INIT;
+ }
// need to check again since the mode could have changed
// while we were waiting
minBufferCountNeeded = mSynchronousMode ?
@@ -228,7 +228,7 @@
((mServerBufferCount != mBufferCount) ||
(mServerBufferCount < minBufferCountNeeded))) {
// here we're guaranteed that mQueue is empty
- freeAllBuffers();
+ freeAllBuffersLocked();
mBufferCount = mServerBufferCount;
if (mBufferCount < minBufferCountNeeded)
mBufferCount = minBufferCountNeeded;
@@ -290,9 +290,12 @@
// for for some buffers to be consumed
tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT);
if (tryAgain) {
- LOGW("Waiting..In synchronous mode and no buffer to dQ");
+ LOGV("Waiting..In synchronous mode and no buffer to dequeue");
mDequeueCondition.wait(mMutex);
}
+ if (mStopped) {
+ return NO_INIT;
+ }
}
if (mSynchronousMode && found == INVALID_BUFFER_SLOT) {
@@ -304,7 +307,7 @@
return -EBUSY;
}
- const int buf = found;
+ const int bufIndex = found;
*outBuf = found;
const bool useDefaultSize = !w && !h;
@@ -322,9 +325,9 @@
// buffer is now in DEQUEUED (but can also be current at the same time,
// if we're in synchronous mode)
- mSlots[buf].mBufferState = BufferSlot::DEQUEUED;
+ mSlots[bufIndex].mBufferState = BufferSlot::DEQUEUED;
- const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer);
+ const sp<GraphicBuffer>& buffer(mSlots[bufIndex].mGraphicBuffer);
if ((buffer == NULL) ||
(uint32_t(buffer->width) != w) ||
(uint32_t(buffer->height) != h) ||
@@ -342,22 +345,25 @@
if (updateFormat) {
mPixelFormat = format;
}
- mSlots[buf].mGraphicBuffer = graphicBuffer;
- mSlots[buf].mRequestBufferCalled = false;
+ mSlots[bufIndex].mGraphicBuffer = graphicBuffer;
+ mSlots[bufIndex].mRequestBufferCalled = false;
returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
}
return returnFlags;
}
+// TODO: clean this up
status_t SurfaceMediaSource::setSynchronousMode(bool enabled) {
Mutex::Autolock lock(mMutex);
+ if (mStopped) {
+ LOGE("setSynchronousMode: SurfaceMediaSource has been stopped!");
+ return NO_INIT;
+ }
- status_t err = OK;
if (!enabled) {
- // going to asynchronous mode, drain the queue
- while (mSynchronousMode != enabled && !mQueue.isEmpty()) {
- mDequeueCondition.wait(mMutex);
- }
+ // Async mode is not allowed
+ LOGE("SurfaceMediaSource can be used only synchronous mode!");
+ return INVALID_OPERATION;
}
if (mSynchronousMode != enabled) {
@@ -368,13 +374,19 @@
mSynchronousMode = enabled;
mDequeueCondition.signal();
}
- return err;
+ return OK;
}
status_t SurfaceMediaSource::connect(int api,
uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
LOGV("SurfaceMediaSource::connect");
Mutex::Autolock lock(mMutex);
+
+ if (mStopped) {
+ LOGE("Connect: SurfaceMediaSource has been stopped!");
+ return NO_INIT;
+ }
+
status_t err = NO_ERROR;
switch (api) {
case NATIVE_WINDOW_API_EGL:
@@ -397,9 +409,25 @@
return err;
}
+// This is called by the client side when it is done
+// TODO: Currently, this also sets mStopped to true which
+// is needed for unblocking the encoder which might be
+// waiting to read more frames. So if on the client side,
+// the same thread supplies the frames and also calls stop
+// on the encoder, the client has to call disconnect before
+// it calls stop.
+// In the case of the camera,
+// that need not be required since the thread supplying the
+// frames is separate than the one calling stop.
status_t SurfaceMediaSource::disconnect(int api) {
LOGV("SurfaceMediaSource::disconnect");
Mutex::Autolock lock(mMutex);
+
+ if (mStopped) {
+ LOGE("disconnect: SurfaceMediaSoource is already stopped!");
+ return NO_INIT;
+ }
+
status_t err = NO_ERROR;
switch (api) {
case NATIVE_WINDOW_API_EGL:
@@ -408,6 +436,9 @@
case NATIVE_WINDOW_API_CAMERA:
if (mConnectedApi == api) {
mConnectedApi = NO_CONNECTED_API;
+ mStopped = true;
+ mDequeueCondition.signal();
+ mFrameAvailableCondition.signal();
} else {
err = -EINVAL;
}
@@ -419,45 +450,47 @@
return err;
}
-status_t SurfaceMediaSource::queueBuffer(int buf, int64_t timestamp,
+status_t SurfaceMediaSource::queueBuffer(int bufIndex, int64_t timestamp,
uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
LOGV("queueBuffer");
Mutex::Autolock lock(mMutex);
- if (buf < 0 || buf >= mBufferCount) {
+ if (bufIndex < 0 || bufIndex >= mBufferCount) {
LOGE("queueBuffer: slot index out of range [0, %d]: %d",
- mBufferCount, buf);
+ mBufferCount, bufIndex);
return -EINVAL;
- } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
+ } else if (mSlots[bufIndex].mBufferState != BufferSlot::DEQUEUED) {
LOGE("queueBuffer: slot %d is not owned by the client (state=%d)",
- buf, mSlots[buf].mBufferState);
+ bufIndex, mSlots[bufIndex].mBufferState);
return -EINVAL;
- } else if (!mSlots[buf].mRequestBufferCalled) {
+ } else if (!mSlots[bufIndex].mRequestBufferCalled) {
LOGE("queueBuffer: slot %d was enqueued without requesting a "
- "buffer", buf);
+ "buffer", bufIndex);
return -EINVAL;
}
if (mSynchronousMode) {
// in synchronous mode we queue all buffers in a FIFO
- mQueue.push_back(buf);
- LOGV("Client queued buffer on slot: %d, Q size = %d",
- buf, mQueue.size());
+ mQueue.push_back(bufIndex);
+ mNumFramesReceived++;
+ LOGV("Client queued buf# %d @slot: %d, Q size = %d, handle = %p, timestamp = %lld",
+ mNumFramesReceived, bufIndex, mQueue.size(),
+ mSlots[bufIndex].mGraphicBuffer->handle, timestamp);
} else {
// in asynchronous mode we only keep the most recent buffer
if (mQueue.empty()) {
- mQueue.push_back(buf);
+ mQueue.push_back(bufIndex);
} else {
Fifo::iterator front(mQueue.begin());
// buffer currently queued is freed
mSlots[*front].mBufferState = BufferSlot::FREE;
// and we record the new buffer index in the queued list
- *front = buf;
+ *front = bufIndex;
}
}
- mSlots[buf].mBufferState = BufferSlot::QUEUED;
- mSlots[buf].mTimestamp = timestamp;
+ mSlots[bufIndex].mBufferState = BufferSlot::QUEUED;
+ mSlots[bufIndex].mTimestamp = timestamp;
// TODO: (Confirm) Don't want to signal dequeue here.
// May be just in asynchronous mode?
// mDequeueCondition.signal();
@@ -482,7 +515,7 @@
// wait to hear from StageFrightRecorder to set the buffer FREE
// Make sure this is called when the mutex is locked
status_t SurfaceMediaSource::onFrameReceivedLocked() {
- LOGV("On Frame Received");
+ LOGV("On Frame Received locked");
// Signal the encoder that a new frame has arrived
mFrameAvailableCondition.signal();
@@ -501,19 +534,19 @@
}
-void SurfaceMediaSource::cancelBuffer(int buf) {
+void SurfaceMediaSource::cancelBuffer(int bufIndex) {
LOGV("SurfaceMediaSource::cancelBuffer");
Mutex::Autolock lock(mMutex);
- if (buf < 0 || buf >= mBufferCount) {
+ if (bufIndex < 0 || bufIndex >= mBufferCount) {
LOGE("cancelBuffer: slot index out of range [0, %d]: %d",
- mBufferCount, buf);
+ mBufferCount, bufIndex);
return;
- } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) {
+ } else if (mSlots[bufIndex].mBufferState != BufferSlot::DEQUEUED) {
LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)",
- buf, mSlots[buf].mBufferState);
+ bufIndex, mSlots[bufIndex].mBufferState);
return;
}
- mSlots[buf].mBufferState = BufferSlot::FREE;
+ mSlots[bufIndex].mBufferState = BufferSlot::FREE;
mDequeueCondition.signal();
}
@@ -531,8 +564,8 @@
mFrameAvailableListener = listener;
}
-void SurfaceMediaSource::freeAllBuffers() {
- LOGV("freeAllBuffers");
+void SurfaceMediaSource::freeAllBuffersLocked() {
+ LOGV("freeAllBuffersLocked");
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
mSlots[i].mGraphicBuffer = 0;
mSlots[i].mBufferState = BufferSlot::FREE;
@@ -648,10 +681,7 @@
status_t SurfaceMediaSource::start(MetaData *params)
{
- LOGV("start");
- Mutex::Autolock lock(mMutex);
- CHECK(!mStarted);
- mStarted = true;
+ LOGV("started!");
return OK;
}
@@ -662,8 +692,11 @@
Mutex::Autolock lock(mMutex);
// TODO: Add waiting on mFrameCompletedCondition here?
- mStarted = false;
+ mStopped = true;
mFrameAvailableCondition.signal();
+ mDequeueCondition.signal();
+ mQueue.clear();
+ freeAllBuffersLocked();
return OK;
}
@@ -688,23 +721,25 @@
}
status_t SurfaceMediaSource::read( MediaBuffer **buffer,
- const ReadOptions *options)
+ const ReadOptions *options)
{
+ Mutex::Autolock autoLock(mMutex) ;
+
LOGV("Read. Size of queued buffer: %d", mQueue.size());
*buffer = NULL;
- Mutex::Autolock autoLock(mMutex) ;
// If the recording has started and the queue is empty, then just
// wait here till the frames come in from the client side
- while (mStarted && mQueue.empty()) {
+ while (!mStopped && mQueue.empty()) {
LOGV("NO FRAMES! Recorder waiting for FrameAvailableCondition");
mFrameAvailableCondition.wait(mMutex);
}
// If the loop was exited as a result of stopping the recording,
// it is OK
- if (!mStarted) {
- return OK;
+ if (mStopped) {
+ LOGV("Read: SurfaceMediaSource is stopped. Returning NO_INIT;");
+ return NO_INIT;
}
// Update the current buffer info
@@ -712,15 +747,20 @@
// can be more than one "current" slots.
Fifo::iterator front(mQueue.begin());
mCurrentSlot = *front;
+ mQueue.erase(front);
mCurrentBuf = mSlots[mCurrentSlot].mGraphicBuffer;
+ int64_t prevTimeStamp = mCurrentTimestamp;
mCurrentTimestamp = mSlots[mCurrentSlot].mTimestamp;
-
+ mNumFramesEncoded++;
// Pass the data to the MediaBuffer. Pass in only the metadata
passMetadataBufferLocked(buffer);
(*buffer)->setObserver(this);
(*buffer)->add_ref();
- (*buffer)->meta_data()->setInt64(kKeyTime, mCurrentTimestamp);
+ (*buffer)->meta_data()->setInt64(kKeyTime, mCurrentTimestamp / 1000);
+ LOGV("Frames encoded = %d, timestamp = %lld, time diff = %lld",
+ mNumFramesEncoded, mCurrentTimestamp / 1000,
+ mCurrentTimestamp / 1000 - prevTimeStamp / 1000);
return OK;
}
@@ -743,15 +783,17 @@
new MediaBuffer(4 + sizeof(buffer_handle_t));
char *data = (char *)tempBuffer->data();
if (data == NULL) {
- LOGE("Cannot allocate memory for passing buffer metadata!");
+ LOGE("Cannot allocate memory for metadata buffer!");
return;
}
OMX_U32 type = kMetadataBufferTypeGrallocSource;
memcpy(data, &type, 4);
memcpy(data + 4, &(mCurrentBuf->handle), sizeof(buffer_handle_t));
*buffer = tempBuffer;
-}
+ LOGV("handle = %p, , offset = %d, length = %d",
+ mCurrentBuf->handle, (*buffer)->range_length(), (*buffer)->range_offset());
+}
void SurfaceMediaSource::signalBufferReturned(MediaBuffer *buffer) {
LOGV("signalBufferReturned");
@@ -759,16 +801,19 @@
bool foundBuffer = false;
Mutex::Autolock autoLock(mMutex);
- if (!mStarted) {
- LOGW("signalBufferReturned: mStarted = false! Nothing to do!");
+ if (mStopped) {
+ LOGV("signalBufferReturned: mStopped = true! Nothing to do!");
return;
}
- for (Fifo::iterator it = mQueue.begin(); it != mQueue.end(); ++it) {
- CHECK(mSlots[*it].mGraphicBuffer != NULL);
- if (checkBufferMatchesSlot(*it, buffer)) {
- mSlots[*it].mBufferState = BufferSlot::FREE;
- mQueue.erase(it);
+ for (int id = 0; id < NUM_BUFFER_SLOTS; id++) {
+ if (mSlots[id].mGraphicBuffer == NULL) {
+ continue;
+ }
+ if (checkBufferMatchesSlot(id, buffer)) {
+ LOGV("Slot %d returned, matches handle = %p", id,
+ mSlots[id].mGraphicBuffer->handle);
+ mSlots[id].mBufferState = BufferSlot::FREE;
buffer->setObserver(0);
buffer->release();
mDequeueCondition.signal();
@@ -792,5 +837,4 @@
return mSlots[slot].mGraphicBuffer->handle == bufferHandle;
}
-
} // end of namespace android
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC.cpp b/media/libstagefright/codecs/aacdec/SoftAAC.cpp
index f0a330f..2abdb56 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC.cpp
@@ -378,24 +378,36 @@
// fall through
}
- if (mUpsamplingFactor == 2) {
- if (mConfig->desiredChannels == 1) {
- memcpy(&mConfig->pOutputBuffer[1024],
- &mConfig->pOutputBuffer[2048],
- numOutBytes * 2);
+ if (decoderErr == MP4AUDEC_SUCCESS || mNumSamplesOutput > 0) {
+ // We'll only output data if we successfully decoded it or
+ // we've previously decoded valid data, in the latter case
+ // (decode failed) we'll output a silent frame.
+
+ if (mUpsamplingFactor == 2) {
+ if (mConfig->desiredChannels == 1) {
+ memcpy(&mConfig->pOutputBuffer[1024],
+ &mConfig->pOutputBuffer[2048],
+ numOutBytes * 2);
+ }
+ numOutBytes *= 2;
}
- numOutBytes *= 2;
+
+ outHeader->nFilledLen = numOutBytes;
+ outHeader->nFlags = 0;
+
+ outHeader->nTimeStamp =
+ mAnchorTimeUs
+ + (mNumSamplesOutput * 1000000ll) / mConfig->samplingRate;
+
+ mNumSamplesOutput += mConfig->frameLength * mUpsamplingFactor;
+
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(outQueue.begin());
+ outInfo = NULL;
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
}
- outHeader->nFilledLen = numOutBytes;
- outHeader->nFlags = 0;
-
- outHeader->nTimeStamp =
- mAnchorTimeUs
- + (mNumSamplesOutput * 1000000ll) / mConfig->samplingRate;
-
- mNumSamplesOutput += mConfig->frameLength * mUpsamplingFactor;
-
if (inHeader->nFilledLen == 0) {
inInfo->mOwnedByUs = false;
inQueue.erase(inQueue.begin());
@@ -404,12 +416,6 @@
inHeader = NULL;
}
- outInfo->mOwnedByUs = false;
- outQueue.erase(outQueue.begin());
- outInfo = NULL;
- notifyFillBufferDone(outHeader);
- outHeader = NULL;
-
if (decoderErr == MP4AUDEC_SUCCESS) {
++mInputBufferCount;
}
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index e1b9991..3ef7b71 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -493,7 +493,8 @@
: mDataSource(source),
mReader(new DataSourceReader(mDataSource)),
mSegment(NULL),
- mExtractedThumbnails(false) {
+ mExtractedThumbnails(false),
+ mIsWebm(false) {
off64_t size;
mIsLiveStreaming =
(mDataSource->flags()
@@ -507,6 +508,10 @@
return;
}
+ if (ebmlHeader.m_docType && !strcmp("webm", ebmlHeader.m_docType)) {
+ mIsWebm = true;
+ }
+
long long ret =
mkvparser::Segment::CreateInstance(mReader, pos, mSegment);
@@ -757,7 +762,10 @@
sp<MetaData> MatroskaExtractor::getMetaData() {
sp<MetaData> meta = new MetaData;
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MATROSKA);
+
+ meta->setCString(
+ kKeyMIMEType,
+ mIsWebm ? "video/webm" : MEDIA_MIMETYPE_CONTAINER_MATROSKA);
return meta;
}
diff --git a/media/libstagefright/matroska/MatroskaExtractor.h b/media/libstagefright/matroska/MatroskaExtractor.h
index 38ebd61..1294b4f 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.h
+++ b/media/libstagefright/matroska/MatroskaExtractor.h
@@ -68,6 +68,7 @@
mkvparser::Segment *mSegment;
bool mExtractedThumbnails;
bool mIsLiveStreaming;
+ bool mIsWebm;
void addTracks();
void findThumbnails();
diff --git a/media/libstagefright/tests/Android.mk b/media/libstagefright/tests/Android.mk
index 3ea8f39..357feb1 100644
--- a/media/libstagefright/tests/Android.mk
+++ b/media/libstagefright/tests/Android.mk
@@ -19,12 +19,13 @@
libbinder \
libcutils \
libgui \
- libstlport \
- libui \
- libutils \
+ libmedia \
libstagefright \
libstagefright_omx \
libstagefright_foundation \
+ libstlport \
+ libui \
+ libutils \
LOCAL_STATIC_LIBRARIES := \
libgtest \
diff --git a/media/libstagefright/tests/SurfaceMediaSource_test.cpp b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
index 5b32b68..d643a0b 100644
--- a/media/libstagefright/tests/SurfaceMediaSource_test.cpp
+++ b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
@@ -14,14 +14,17 @@
* limitations under the License.
*/
-#define LOG_TAG "SurfaceMediaSource_test"
// #define LOG_NDEBUG 0
+#define LOG_TAG "SurfaceMediaSource_test"
#include <gtest/gtest.h>
#include <utils/String8.h>
#include <utils/Errors.h>
+#include <fcntl.h>
+#include <unistd.h>
#include <media/stagefright/SurfaceMediaSource.h>
+#include <media/mediarecorder.h>
#include <gui/SurfaceTextureClient.h>
#include <ui/GraphicBuffer.h>
@@ -33,24 +36,322 @@
#include <ui/FramebufferNativeWindow.h>
#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
-#include <media/stagefright/MPEG4Writer.h>
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXCodec.h>
#include <OMX_Component.h>
#include "DummyRecorder.h"
+
namespace android {
+class GLTest : public ::testing::Test {
+protected:
+ GLTest():
+ mEglDisplay(EGL_NO_DISPLAY),
+ mEglSurface(EGL_NO_SURFACE),
+ mEglContext(EGL_NO_CONTEXT) {
+ }
+
+ virtual void SetUp() {
+ LOGV("GLTest::SetUp()");
+ mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay);
+
+ EGLint majorVersion;
+ EGLint minorVersion;
+ EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ RecordProperty("EglVersionMajor", majorVersion);
+ RecordProperty("EglVersionMajor", minorVersion);
+
+ EGLint numConfigs = 0;
+ EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mGlConfig,
+ 1, &numConfigs));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ char* displaySecsEnv = getenv("GLTEST_DISPLAY_SECS");
+ if (displaySecsEnv != NULL) {
+ mDisplaySecs = atoi(displaySecsEnv);
+ if (mDisplaySecs < 0) {
+ mDisplaySecs = 0;
+ }
+ } else {
+ mDisplaySecs = 0;
+ }
+
+ if (mDisplaySecs > 0) {
+ mComposerClient = new SurfaceComposerClient;
+ ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+ mSurfaceControl = mComposerClient->createSurface(
+ String8("Test Surface"), 0,
+ getSurfaceWidth(), getSurfaceHeight(),
+ PIXEL_FORMAT_RGB_888, 0);
+
+ ASSERT_TRUE(mSurfaceControl != NULL);
+ ASSERT_TRUE(mSurfaceControl->isValid());
+
+ SurfaceComposerClient::openGlobalTransaction();
+ ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF));
+ ASSERT_EQ(NO_ERROR, mSurfaceControl->show());
+ SurfaceComposerClient::closeGlobalTransaction();
+
+ sp<ANativeWindow> window = mSurfaceControl->getSurface();
+ mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
+ window.get(), NULL);
+ } else {
+ EGLint pbufferAttribs[] = {
+ EGL_WIDTH, getSurfaceWidth(),
+ EGL_HEIGHT, getSurfaceHeight(),
+ EGL_NONE };
+
+ mEglSurface = eglCreatePbufferSurface(mEglDisplay, mGlConfig,
+ pbufferAttribs);
+ }
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
+
+ mEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT,
+ getContextAttribs());
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
+
+ EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
+ mEglContext));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ EGLint w, h;
+ EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &w));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &h));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ RecordProperty("EglSurfaceWidth", w);
+ RecordProperty("EglSurfaceHeight", h);
+
+ glViewport(0, 0, w, h);
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ }
+
+ virtual void TearDown() {
+ // Display the result
+ if (mDisplaySecs > 0 && mEglSurface != EGL_NO_SURFACE) {
+ eglSwapBuffers(mEglDisplay, mEglSurface);
+ sleep(mDisplaySecs);
+ }
+
+ if (mComposerClient != NULL) {
+ mComposerClient->dispose();
+ }
+ if (mEglContext != EGL_NO_CONTEXT) {
+ eglDestroyContext(mEglDisplay, mEglContext);
+ }
+ if (mEglSurface != EGL_NO_SURFACE) {
+ eglDestroySurface(mEglDisplay, mEglSurface);
+ }
+ if (mEglDisplay != EGL_NO_DISPLAY) {
+ eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
+ eglTerminate(mEglDisplay);
+ }
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ }
+
+ virtual EGLint const* getConfigAttribs() {
+ LOGV("GLTest getConfigAttribs");
+ static EGLint sDefaultConfigAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_DEPTH_SIZE, 16,
+ EGL_STENCIL_SIZE, 8,
+ EGL_NONE };
+
+ return sDefaultConfigAttribs;
+ }
+
+ virtual EGLint const* getContextAttribs() {
+ static EGLint sDefaultContextAttribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE };
+
+ return sDefaultContextAttribs;
+ }
+
+ virtual EGLint getSurfaceWidth() {
+ return 512;
+ }
+
+ virtual EGLint getSurfaceHeight() {
+ return 512;
+ }
+
+ void loadShader(GLenum shaderType, const char* pSource, GLuint* outShader) {
+ GLuint shader = glCreateShader(shaderType);
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ if (shader) {
+ glShaderSource(shader, 1, &pSource, NULL);
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ glCompileShader(shader);
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ GLint compiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ if (!compiled) {
+ GLint infoLen = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ if (infoLen) {
+ char* buf = (char*) malloc(infoLen);
+ if (buf) {
+ glGetShaderInfoLog(shader, infoLen, NULL, buf);
+ printf("Shader compile log:\n%s\n", buf);
+ free(buf);
+ FAIL();
+ }
+ } else {
+ char* buf = (char*) malloc(0x1000);
+ if (buf) {
+ glGetShaderInfoLog(shader, 0x1000, NULL, buf);
+ printf("Shader compile log:\n%s\n", buf);
+ free(buf);
+ FAIL();
+ }
+ }
+ glDeleteShader(shader);
+ shader = 0;
+ }
+ }
+ ASSERT_TRUE(shader != 0);
+ *outShader = shader;
+ }
+
+ void createProgram(const char* pVertexSource, const char* pFragmentSource,
+ GLuint* outPgm) {
+ GLuint vertexShader, fragmentShader;
+ {
+ SCOPED_TRACE("compiling vertex shader");
+ loadShader(GL_VERTEX_SHADER, pVertexSource, &vertexShader);
+ if (HasFatalFailure()) {
+ return;
+ }
+ }
+ {
+ SCOPED_TRACE("compiling fragment shader");
+ loadShader(GL_FRAGMENT_SHADER, pFragmentSource, &fragmentShader);
+ if (HasFatalFailure()) {
+ return;
+ }
+ }
+
+ GLuint program = glCreateProgram();
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ if (program) {
+ glAttachShader(program, vertexShader);
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ glAttachShader(program, fragmentShader);
+ ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
+ glLinkProgram(program);
+ GLint linkStatus = GL_FALSE;
+ glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
+ if (linkStatus != GL_TRUE) {
+ GLint bufLength = 0;
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
+ if (bufLength) {
+ char* buf = (char*) malloc(bufLength);
+ if (buf) {
+ glGetProgramInfoLog(program, bufLength, NULL, buf);
+ printf("Program link log:\n%s\n", buf);
+ free(buf);
+ FAIL();
+ }
+ }
+ glDeleteProgram(program);
+ program = 0;
+ }
+ }
+ glDeleteShader(vertexShader);
+ glDeleteShader(fragmentShader);
+ ASSERT_TRUE(program != 0);
+ *outPgm = program;
+ }
+
+ static int abs(int value) {
+ return value > 0 ? value : -value;
+ }
+
+ ::testing::AssertionResult checkPixel(int x, int y, int r,
+ int g, int b, int a, int tolerance=2) {
+ GLubyte pixel[4];
+ String8 msg;
+ glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
+ GLenum err = glGetError();
+ if (err != GL_NO_ERROR) {
+ msg += String8::format("error reading pixel: %#x", err);
+ while ((err = glGetError()) != GL_NO_ERROR) {
+ msg += String8::format(", %#x", err);
+ }
+ fprintf(stderr, "pixel check failure: %s\n", msg.string());
+ return ::testing::AssertionFailure(
+ ::testing::Message(msg.string()));
+ }
+ if (r >= 0 && abs(r - int(pixel[0])) > tolerance) {
+ msg += String8::format("r(%d isn't %d)", pixel[0], r);
+ }
+ if (g >= 0 && abs(g - int(pixel[1])) > tolerance) {
+ if (!msg.isEmpty()) {
+ msg += " ";
+ }
+ msg += String8::format("g(%d isn't %d)", pixel[1], g);
+ }
+ if (b >= 0 && abs(b - int(pixel[2])) > tolerance) {
+ if (!msg.isEmpty()) {
+ msg += " ";
+ }
+ msg += String8::format("b(%d isn't %d)", pixel[2], b);
+ }
+ if (a >= 0 && abs(a - int(pixel[3])) > tolerance) {
+ if (!msg.isEmpty()) {
+ msg += " ";
+ }
+ msg += String8::format("a(%d isn't %d)", pixel[3], a);
+ }
+ if (!msg.isEmpty()) {
+ fprintf(stderr, "pixel check failure: %s\n", msg.string());
+ return ::testing::AssertionFailure(
+ ::testing::Message(msg.string()));
+ } else {
+ return ::testing::AssertionSuccess();
+ }
+ }
+
+ int mDisplaySecs;
+ sp<SurfaceComposerClient> mComposerClient;
+ sp<SurfaceControl> mSurfaceControl;
+
+ EGLDisplay mEglDisplay;
+ EGLSurface mEglSurface;
+ EGLContext mEglContext;
+ EGLConfig mGlConfig;
+};
+
+///////////////////////////////////////////////////////////////////////
+// Class for the NON-GL tests
+///////////////////////////////////////////////////////////////////////
class SurfaceMediaSourceTest : public ::testing::Test {
public:
- SurfaceMediaSourceTest( ): mYuvTexWidth(64), mYuvTexHeight(66) { }
- sp<MPEG4Writer> setUpWriter(OMXClient &client );
+ SurfaceMediaSourceTest( ): mYuvTexWidth(176), mYuvTexHeight(144) { }
void oneBufferPass(int width, int height );
+ void oneBufferPassNoFill(int width, int height );
static void fillYV12Buffer(uint8_t* buf, int w, int h, int stride) ;
static void fillYV12BufferRect(uint8_t* buf, int w, int h,
int stride, const android_native_rect_t& rect) ;
@@ -62,10 +363,8 @@
mSMS->setSynchronousMode(true);
mSTC = new SurfaceTextureClient(mSMS);
mANW = mSTC;
-
}
-
virtual void TearDown() {
mSMS.clear();
mSTC.clear();
@@ -78,11 +377,142 @@
sp<SurfaceMediaSource> mSMS;
sp<SurfaceTextureClient> mSTC;
sp<ANativeWindow> mANW;
-
};
+///////////////////////////////////////////////////////////////////////
+// Class for the GL tests
+///////////////////////////////////////////////////////////////////////
+class SurfaceMediaSourceGLTest : public GLTest {
+public:
+
+ SurfaceMediaSourceGLTest( ): mYuvTexWidth(176), mYuvTexHeight(144) { }
+ virtual EGLint const* getConfigAttribs();
+ void oneBufferPassGL(int num = 0);
+ static sp<MediaRecorder> setUpMediaRecorder(int fileDescriptor, int videoSource,
+ int outputFormat, int videoEncoder, int width, int height, int fps);
+protected:
+
+ virtual void SetUp() {
+ LOGV("SMS-GLTest::SetUp()");
+ android::ProcessState::self()->startThreadPool();
+ mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight);
+ mSTC = new SurfaceTextureClient(mSMS);
+ mANW = mSTC;
+
+ // Doing the setup related to the GL Side
+ GLTest::SetUp();
+ }
+
+ virtual void TearDown() {
+ mSMS.clear();
+ mSTC.clear();
+ mANW.clear();
+ GLTest::TearDown();
+ eglDestroySurface(mEglDisplay, mSmsEglSurface);
+ }
+
+ void setUpEGLSurfaceFromMediaRecorder(sp<MediaRecorder>& mr);
+
+ const int mYuvTexWidth;
+ const int mYuvTexHeight;
+
+ sp<SurfaceMediaSource> mSMS;
+ sp<SurfaceTextureClient> mSTC;
+ sp<ANativeWindow> mANW;
+ EGLConfig mSMSGlConfig;
+ EGLSurface mSmsEglSurface;
+};
+
+/////////////////////////////////////////////////////////////////////
+// Methods in SurfaceMediaSourceGLTest
+/////////////////////////////////////////////////////////////////////
+EGLint const* SurfaceMediaSourceGLTest::getConfigAttribs() {
+ LOGV("SurfaceMediaSourceGLTest getConfigAttribs");
+ static EGLint sDefaultConfigAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_RECORDABLE_ANDROID, EGL_TRUE,
+ EGL_NONE };
+
+ return sDefaultConfigAttribs;
+}
+
+// One pass of dequeuing and queuing a GLBuffer
+void SurfaceMediaSourceGLTest::oneBufferPassGL(int num) {
+ int d = num % 50;
+ float f = 0.2f; // 0.1f * d;
+
+ glClearColor(0, 0.3, 0, 0.6);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glEnable(GL_SCISSOR_TEST);
+ glScissor(4 + d, 4 + d, 4, 4);
+ glClearColor(1.0 - f, f, f, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glScissor(24 + d, 48 + d, 4, 4);
+ glClearColor(f, 1.0 - f, f, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glScissor(37 + d, 17 + d, 4, 4);
+ glClearColor(f, f, 1.0 - f, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ // The following call dequeues and queues the buffer
+ eglSwapBuffers(mEglDisplay, mSmsEglSurface);
+ glDisable(GL_SCISSOR_TEST);
+}
+
+// Set up the MediaRecorder which runs in the same process as mediaserver
+sp<MediaRecorder> SurfaceMediaSourceGLTest::setUpMediaRecorder(int fd, int videoSource,
+ int outputFormat, int videoEncoder, int width, int height, int fps) {
+ sp<MediaRecorder> mr = new MediaRecorder();
+ mr->setVideoSource(videoSource);
+ mr->setOutputFormat(outputFormat);
+ mr->setVideoEncoder(videoEncoder);
+ mr->setOutputFile(fd, 0, 0);
+ mr->setVideoSize(width, height);
+ mr->setVideoFrameRate(fps);
+ mr->prepare();
+ LOGV("Starting MediaRecorder...");
+ CHECK_EQ(OK, mr->start());
+ return mr;
+}
+
+// query the mediarecorder for a surfacemeidasource and create an egl surface with that
+void SurfaceMediaSourceGLTest::setUpEGLSurfaceFromMediaRecorder(sp<MediaRecorder>& mr) {
+ sp<ISurfaceTexture> iST = mr->querySurfaceMediaSourceFromMediaServer();
+ mSTC = new SurfaceTextureClient(iST);
+ mANW = mSTC;
+
+ EGLint numConfigs = 0;
+ EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mSMSGlConfig,
+ 1, &numConfigs));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ LOGV("Native Window = %p, mSTC = %p", mANW.get(), mSTC.get());
+
+ mSmsEglSurface = eglCreateWindowSurface(mEglDisplay, mSMSGlConfig,
+ mANW.get(), NULL);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_SURFACE, mSmsEglSurface) ;
+
+ EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mSmsEglSurface, mSmsEglSurface,
+ mEglContext));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+}
+
+
+/////////////////////////////////////////////////////////////////////
+// Methods in SurfaceMediaSourceTest
+/////////////////////////////////////////////////////////////////////
+
+// One pass of dequeuing and queuing the buffer. Fill it in with
+// cpu YV12 buffer
void SurfaceMediaSourceTest::oneBufferPass(int width, int height ) {
- LOGV("One Buffer Pass");
ANativeWindowBuffer* anb;
ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
ASSERT_TRUE(anb != NULL);
@@ -99,42 +529,16 @@
ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer()));
}
-sp<MPEG4Writer> SurfaceMediaSourceTest::setUpWriter(OMXClient &client ) {
- // Writing to a file
- const char *fileName = "/sdcard/outputSurfEnc.mp4";
- sp<MetaData> enc_meta = new MetaData;
- enc_meta->setInt32(kKeyBitRate, 300000);
- enc_meta->setInt32(kKeyFrameRate, 30);
+// Dequeuing and queuing the buffer without really filling it in.
+void SurfaceMediaSourceTest::oneBufferPassNoFill(int width, int height ) {
+ ANativeWindowBuffer* anb;
+ ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
+ ASSERT_TRUE(anb != NULL);
- enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
-
- sp<MetaData> meta = mSMS->getFormat();
-
- int32_t width, height, stride, sliceHeight, colorFormat;
- CHECK(meta->findInt32(kKeyWidth, &width));
- CHECK(meta->findInt32(kKeyHeight, &height));
- CHECK(meta->findInt32(kKeyStride, &stride));
- CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight));
- CHECK(meta->findInt32(kKeyColorFormat, &colorFormat));
-
- enc_meta->setInt32(kKeyWidth, width);
- enc_meta->setInt32(kKeyHeight, height);
- enc_meta->setInt32(kKeyIFramesInterval, 1);
- enc_meta->setInt32(kKeyStride, stride);
- enc_meta->setInt32(kKeySliceHeight, sliceHeight);
- // TODO: overwriting the colorformat since the format set by GRAlloc
- // could be wrong or not be read by OMX
- enc_meta->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420Planar);
-
-
- sp<MediaSource> encoder =
- OMXCodec::Create(
- client.interface(), enc_meta, true /* createEncoder */, mSMS);
-
- sp<MPEG4Writer> writer = new MPEG4Writer(fileName);
- writer->addSource(encoder);
-
- return writer;
+ sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+ // ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer()));
+ // We do not fill the buffer in. Just queue it back.
+ ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer()));
}
// Fill a YV12 buffer with a multi-colored checkerboard pattern
@@ -216,46 +620,53 @@
return OK;
}
};
-
///////////////////////////////////////////////////////////////////
// TESTS
+// SurfaceMediaSourceTest class contains tests that fill the buffers
+// using the cpu calls
+// SurfaceMediaSourceGLTest class contains tests that fill the buffers
+// using the GL calls.
+// TODO: None of the tests actually verify the encoded images.. so at this point,
+// these are mostly functionality tests + visual inspection
+//////////////////////////////////////////////////////////////////////
+
// Just pass one buffer from the native_window to the SurfaceMediaSource
-TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotOneBufferPass) {
+// Dummy Encoder
+static int testId = 1;
+TEST_F(SurfaceMediaSourceTest, DISABLED_DummyEncodingFromCpuFilledYV12BufferNpotOneBufferPass) {
+ LOGV("Test # %d", testId++);
LOGV("Testing OneBufferPass ******************************");
- ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
- 0, 0, HAL_PIXEL_FORMAT_YV12));
- ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
- GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
-
+ ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
+ HAL_PIXEL_FORMAT_YV12));
oneBufferPass(mYuvTexWidth, mYuvTexHeight);
}
// Pass the buffer with the wrong height and weight and should not be accepted
-TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotWrongSizeBufferPass) {
+// Dummy Encoder
+TEST_F(SurfaceMediaSourceTest, DISABLED_DummyEncodingFromCpuFilledYV12BufferNpotWrongSizeBufferPass) {
+ LOGV("Test # %d", testId++);
LOGV("Testing Wrong size BufferPass ******************************");
// setting the client side buffer size different than the server size
- ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
- 10, 10, HAL_PIXEL_FORMAT_YV12));
- ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
- GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
+ ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(),
+ 10, 10));
+ ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
+ HAL_PIXEL_FORMAT_YV12));
ANativeWindowBuffer* anb;
- // make sure we get an error back when dequeuing!
+ // Note: make sure we get an ERROR back when dequeuing!
ASSERT_NE(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
}
-
// pass multiple buffers from the native_window the SurfaceMediaSource
-// A dummy writer is used to simulate actual MPEG4Writer
-TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotMultiBufferPass) {
+// Dummy Encoder
+TEST_F(SurfaceMediaSourceTest, DISABLED_DummyEncodingFromCpuFilledYV12BufferNpotMultiBufferPass) {
+ LOGV("Test # %d", testId++);
LOGV("Testing MultiBufferPass, Dummy Recorder *********************");
- ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
- 0, 0, HAL_PIXEL_FORMAT_YV12));
- ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
- GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
+ ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
+ HAL_PIXEL_FORMAT_YV12));
SimpleDummyRecorder writer(mSMS);
writer.start();
@@ -272,14 +683,13 @@
}
// Delayed pass of multiple buffers from the native_window the SurfaceMediaSource
-// A dummy writer is used to simulate actual MPEG4Writer
-TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotMultiBufferPassLag) {
+// Dummy Encoder
+TEST_F(SurfaceMediaSourceTest, DISABLED_DummyLagEncodingFromCpuFilledYV12BufferNpotMultiBufferPass) {
+ LOGV("Test # %d", testId++);
LOGV("Testing MultiBufferPass, Dummy Recorder Lagging **************");
- ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
- 0, 0, HAL_PIXEL_FORMAT_YV12));
- ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
- GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
+ ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
+ HAL_PIXEL_FORMAT_YV12));
SimpleDummyRecorder writer(mSMS);
writer.start();
@@ -299,12 +709,11 @@
// pass multiple buffers from the native_window the SurfaceMediaSource
// A dummy writer (MULTITHREADED) is used to simulate actual MPEG4Writer
-TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotMultiBufferPassThreaded) {
+TEST_F(SurfaceMediaSourceTest, DISABLED_DummyThreadedEncodingFromCpuFilledYV12BufferNpotMultiBufferPass) {
+ LOGV("Test # %d", testId++);
LOGV("Testing MultiBufferPass, Dummy Recorder Multi-Threaded **********");
- ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
- 0, 0, HAL_PIXEL_FORMAT_YV12));
- ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
- GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
+ ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
+ HAL_PIXEL_FORMAT_YV12));
DummyRecorder writer(mSMS);
writer.start();
@@ -318,32 +727,210 @@
writer.stop();
}
-// Test to examine the actual encoding. Temporarily disabled till the
-// colorformat and encoding from GRAlloc data is resolved
-TEST_F(SurfaceMediaSourceTest, DISABLED_EncodingFromCpuFilledYV12BufferNpotWrite) {
- LOGV("Testing the whole pipeline with actual Recorder");
- ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
- 0, 0, HAL_PIXEL_FORMAT_YV12));
- ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
- GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
- OMXClient client;
- CHECK_EQ(OK, client.connect());
+// Test to examine actual encoding using mediarecorder
+// We use the mediaserver to create a mediarecorder and send
+// it back to us. So SurfaceMediaSource lives in the same process
+// as the mediaserver.
+// Very close to the actual camera, except that the
+// buffers are filled and queueud by the CPU instead of GL.
+TEST_F(SurfaceMediaSourceTest, DISABLED_EncodingFromCpuYV12BufferNpotWriteMediaServer) {
+ LOGV("Test # %d", testId++);
+ LOGV("************** Testing the whole pipeline with actual MediaRecorder ***********");
+ LOGV("************** SurfaceMediaSource is same process as mediaserver ***********");
- sp<MPEG4Writer> writer = setUpWriter(client);
- int64_t start = systemTime();
- CHECK_EQ(OK, writer->start());
+ const char *fileName = "/sdcard/outputSurfEncMSource.mp4";
+ int fd = open(fileName, O_RDWR | O_CREAT, 0744);
+ if (fd < 0) {
+ LOGE("ERROR: Could not open the the file %s, fd = %d !!", fileName, fd);
+ }
+ CHECK(fd >= 0);
+
+ sp<MediaRecorder> mr = SurfaceMediaSourceGLTest::setUpMediaRecorder(fd,
+ VIDEO_SOURCE_GRALLOC_BUFFER,
+ OUTPUT_FORMAT_MPEG_4, VIDEO_ENCODER_H264, mYuvTexWidth,
+ mYuvTexHeight, 30);
+ // get the reference to the surfacemediasource living in
+ // mediaserver that is created by stagefrightrecorder
+ sp<ISurfaceTexture> iST = mr->querySurfaceMediaSourceFromMediaServer();
+ mSTC = new SurfaceTextureClient(iST);
+ mANW = mSTC;
+ ASSERT_EQ(NO_ERROR, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU));
+ ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(),
+ HAL_PIXEL_FORMAT_YV12));
int32_t nFramesCount = 0;
while (nFramesCount <= 300) {
- oneBufferPass(mYuvTexWidth, mYuvTexHeight);
+ oneBufferPassNoFill(mYuvTexWidth, mYuvTexHeight);
nFramesCount++;
+ LOGV("framesCount = %d", nFramesCount);
}
- CHECK_EQ(OK, writer->stop());
- writer.clear();
- int64_t end = systemTime();
- client.disconnect();
+ ASSERT_EQ(NO_ERROR, native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU));
+ LOGV("Stopping MediaRecorder...");
+ CHECK_EQ(OK, mr->stop());
+ mr.clear();
+ close(fd);
}
+//////////////////////////////////////////////////////////////////////
+// GL tests
+/////////////////////////////////////////////////////////////////////
+// Test to examine whether we can choose the Recordable Android GLConfig
+// DummyRecorder used- no real encoding here
+TEST_F(SurfaceMediaSourceGLTest, ChooseAndroidRecordableEGLConfigDummyWrite) {
+ LOGV("Test # %d", testId++);
+ LOGV("Test to verify creating a surface w/ right config *********");
+
+ mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight);
+ mSTC = new SurfaceTextureClient(mSMS);
+ mANW = mSTC;
+
+ DummyRecorder writer(mSMS);
+ writer.start();
+
+ EGLint numConfigs = 0;
+ EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mSMSGlConfig,
+ 1, &numConfigs));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ mSmsEglSurface = eglCreateWindowSurface(mEglDisplay, mSMSGlConfig,
+ mANW.get(), NULL);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_SURFACE, mSmsEglSurface) ;
+
+ EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mSmsEglSurface, mSmsEglSurface,
+ mEglContext));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ int32_t nFramesCount = 0;
+ while (nFramesCount <= 300) {
+ oneBufferPassGL();
+ nFramesCount++;
+ LOGV("framesCount = %d", nFramesCount);
+ }
+
+ ASSERT_EQ(NO_ERROR, native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_EGL));
+ writer.stop();
+}
+// Test to examine whether we can render GL buffers in to the surface
+// created with the native window handle
+TEST_F(SurfaceMediaSourceGLTest, RenderingToRecordableEGLSurfaceWorks) {
+ LOGV("Test # %d", testId++);
+ LOGV("RenderingToRecordableEGLSurfaceWorks *********************");
+ // Do the producer side of things
+ glClearColor(0.6, 0.6, 0.6, 0.6);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glEnable(GL_SCISSOR_TEST);
+ glScissor(4, 4, 4, 4);
+ glClearColor(1.0, 0.0, 0.0, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glScissor(24, 48, 4, 4);
+ glClearColor(0.0, 1.0, 0.0, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glScissor(37, 17, 4, 4);
+ glClearColor(0.0, 0.0, 1.0, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ EXPECT_TRUE(checkPixel( 0, 0, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel(63, 0, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel(63, 63, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel( 0, 63, 153, 153, 153, 153));
+
+ EXPECT_TRUE(checkPixel( 4, 7, 255, 0, 0, 255));
+ EXPECT_TRUE(checkPixel(25, 51, 0, 255, 0, 255));
+ EXPECT_TRUE(checkPixel(40, 19, 0, 0, 255, 255));
+ EXPECT_TRUE(checkPixel(29, 51, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel( 5, 32, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel(13, 8, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel(46, 3, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel(30, 33, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel( 6, 52, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel(55, 33, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel(16, 29, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel( 1, 30, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel(41, 37, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel(46, 29, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel(15, 25, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel( 3, 52, 153, 153, 153, 153));
+}
+
+// Test to examine the actual encoding with GL buffers
+// Actual encoder, Actual GL Buffers Filled SurfaceMediaSource
+// The same pattern is rendered every frame
+TEST_F(SurfaceMediaSourceGLTest, EncodingFromGLRgbaSameImageEachBufNpotWrite) {
+ LOGV("Test # %d", testId++);
+ LOGV("************** Testing the whole pipeline with actual Recorder ***********");
+ LOGV("************** GL Filling the buffers ***********");
+ // Note: No need to set the colorformat for the buffers. The colorformat is
+ // in the GRAlloc buffers itself.
+
+ const char *fileName = "/sdcard/outputSurfEncMSourceGL.mp4";
+ int fd = open(fileName, O_RDWR | O_CREAT, 0744);
+ if (fd < 0) {
+ LOGE("ERROR: Could not open the the file %s, fd = %d !!", fileName, fd);
+ }
+ CHECK(fd >= 0);
+
+ sp<MediaRecorder> mr = setUpMediaRecorder(fd, VIDEO_SOURCE_GRALLOC_BUFFER,
+ OUTPUT_FORMAT_MPEG_4, VIDEO_ENCODER_H264, mYuvTexWidth, mYuvTexHeight, 30);
+
+ // get the reference to the surfacemediasource living in
+ // mediaserver that is created by stagefrightrecorder
+ setUpEGLSurfaceFromMediaRecorder(mr);
+
+ int32_t nFramesCount = 0;
+ while (nFramesCount <= 300) {
+ oneBufferPassGL();
+ nFramesCount++;
+ LOGV("framesCount = %d", nFramesCount);
+ }
+
+ ASSERT_EQ(NO_ERROR, native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_EGL));
+ LOGV("Stopping MediaRecorder...");
+ CHECK_EQ(OK, mr->stop());
+ mr.clear();
+ close(fd);
+}
+
+// Test to examine the actual encoding from the GL Buffers
+// Actual encoder, Actual GL Buffers Filled SurfaceMediaSource
+// A different pattern is rendered every frame
+TEST_F(SurfaceMediaSourceGLTest, EncodingFromGLRgbaDiffImageEachBufNpotWrite) {
+ LOGV("Test # %d", testId++);
+ LOGV("************** Testing the whole pipeline with actual Recorder ***********");
+ LOGV("************** Diff GL Filling the buffers ***********");
+ // Note: No need to set the colorformat for the buffers. The colorformat is
+ // in the GRAlloc buffers itself.
+
+ const char *fileName = "/sdcard/outputSurfEncMSourceGLDiff.mp4";
+ int fd = open(fileName, O_RDWR | O_CREAT, 0744);
+ if (fd < 0) {
+ LOGE("ERROR: Could not open the the file %s, fd = %d !!", fileName, fd);
+ }
+ CHECK(fd >= 0);
+
+ sp<MediaRecorder> mr = setUpMediaRecorder(fd, VIDEO_SOURCE_GRALLOC_BUFFER,
+ OUTPUT_FORMAT_MPEG_4, VIDEO_ENCODER_H264, mYuvTexWidth, mYuvTexHeight, 30);
+
+ // get the reference to the surfacemediasource living in
+ // mediaserver that is created by stagefrightrecorder
+ setUpEGLSurfaceFromMediaRecorder(mr);
+
+ int32_t nFramesCount = 0;
+ while (nFramesCount <= 300) {
+ oneBufferPassGL(nFramesCount);
+ nFramesCount++;
+ LOGV("framesCount = %d", nFramesCount);
+ }
+
+ ASSERT_EQ(NO_ERROR, native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_EGL));
+ LOGV("Stopping MediaRecorder...");
+ CHECK_EQ(OK, mr->stop());
+ mr.clear();
+ close(fd);
+}
} // namespace android
diff --git a/packages/SystemUI/res/anim/recent_appear.xml b/packages/SystemUI/res/anim/recent_appear.xml
index 4400d9d..20fe052 100644
--- a/packages/SystemUI/res/anim/recent_appear.xml
+++ b/packages/SystemUI/res/anim/recent_appear.xml
@@ -16,5 +16,5 @@
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:fromAlpha="0.0" android:toAlpha="1.0"
- android:duration="@android:integer/config_shortAnimTime"
+ android:duration="@android:integer/config_mediumAnimTime"
/>
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_ticker_tile.png b/packages/SystemUI/res/drawable-hdpi/status_bar_ticker_tile.png
deleted file mode 100644
index 3b826a9..0000000
--- a/packages/SystemUI/res/drawable-hdpi/status_bar_ticker_tile.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_ticker_tile.png b/packages/SystemUI/res/drawable-mdpi/status_bar_ticker_tile.png
deleted file mode 100644
index 9999598..0000000
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_ticker_tile.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/status_bar_ticker_tile.png b/packages/SystemUI/res/drawable-xhdpi/status_bar_ticker_tile.png
deleted file mode 100644
index 6585ad6..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/status_bar_ticker_tile.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable/status_bar_ticker_background.xml b/packages/SystemUI/res/drawable/status_bar_ticker_background.xml
index 83524a6..7cb64c0 100644
--- a/packages/SystemUI/res/drawable/status_bar_ticker_background.xml
+++ b/packages/SystemUI/res/drawable/status_bar_ticker_background.xml
@@ -17,5 +17,5 @@
<bitmap
xmlns:android="http://schemas.android.com/apk/res/android"
android:tileMode="repeat"
- android:src="@drawable/status_bar_ticker_tile"
+ android:src="@*android:drawable/notify_panel_notification_icon_bg"
/>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index b63afbe..b5274a3 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -28,14 +28,15 @@
<RelativeLayout
android:layout_width="match_parent"
- android:layout_height="55dp"
+ android:layout_height="52dp"
android:paddingTop="3dp"
android:paddingBottom="5dp"
android:paddingRight="3dp"
>
<com.android.systemui.statusbar.policy.DateView android:id="@+id/date"
android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent.Title"
- android:textColor="@android:color/holo_blue_bright"
+ android:textColor="@android:color/holo_blue_light"
+ android:textStyle="normal"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
@@ -77,7 +78,7 @@
<View
android:layout_width="match_parent"
- android:layout_height="3dp"
+ android:layout_height="2dp"
android:background="@drawable/status_bar_hr"
/>
@@ -92,7 +93,7 @@
android:textAppearance="@android:style/TextAppearance.Large"
android:padding="8dp"
android:layout_gravity="top"
- android:gravity="center"
+ android:gravity="left"
android:text="@string/status_bar_no_notifications_title"
/>
diff --git a/packages/SystemUI/res/layout/status_bar_icon.xml b/packages/SystemUI/res/layout/status_bar_icon.xml
index 21d606f..d2ebf9e 100644
--- a/packages/SystemUI/res/layout/status_bar_icon.xml
+++ b/packages/SystemUI/res/layout/status_bar_icon.xml
@@ -19,7 +19,7 @@
-->
<!-- The icons are a fixed size so an app can't mess everything up with bogus images -->
-<!-- TODO: the icons are hard coded to 25x25 pixels. Their size should come froem a theme -->
+<!-- TODO: the icons are hard coded to 25x25 pixels. Their size should come from a theme -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="25dp"
android:layout_height="25dp"
@@ -43,4 +43,4 @@
android:textStyle="bold"
/>
-</FrameLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index ca584ab..d627dc4 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -32,13 +32,14 @@
android:layout_alignParentRight="true"
android:focusable="true"
android:clickable="true"
+ android:background="@drawable/notification_item_background_color"
/>
<View
android:layout_width="match_parent"
- android:layout_height="1dp"
+ android:layout_height="@dimen/notification_divider_height"
android:layout_alignParentBottom="true"
- android:background="@android:drawable/divider_horizontal_dark"
+ android:background="@drawable/status_bar_notification_row_background_color"
/>
</RelativeLayout>
diff --git a/packages/SystemUI/res/values-hdpi/dimens.xml b/packages/SystemUI/res/values-hdpi/dimens.xml
index 741b75a..287e0d1 100644
--- a/packages/SystemUI/res/values-hdpi/dimens.xml
+++ b/packages/SystemUI/res/values-hdpi/dimens.xml
@@ -21,4 +21,12 @@
<dimen name="recents_thumbnail_bg_padding_top">7px</dimen>
<dimen name="recents_thumbnail_bg_padding_right">6px</dimen>
<dimen name="recents_thumbnail_bg_padding_bottom">6px</dimen>
+
+ <!-- thickness (height) of each notification row, including any separators or padding -->
+ <!-- Note: this is 64dip + 1px divider = 97px. -->
+ <dimen name="notification_height">97px</dimen>
+
+ <!-- thickness (height) of dividers between each notification row; see math for
+ notification_height above -->
+ <dimen name="notification_divider_height">1px</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index fd5fe7a..5d14fa8 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -18,8 +18,9 @@
-->
<resources>
<drawable name="notification_number_text_color">#ffffffff</drawable>
- <drawable name="notification_item_background_color">#ff000000</drawable>
+ <drawable name="notification_item_background_color">#ff111111</drawable>
<drawable name="ticker_background_color">#ff1d1d1d</drawable>
- <drawable name="status_bar_background">#000000</drawable>
+ <drawable name="status_bar_background">#ff000000</drawable>
<drawable name="status_bar_recents_background">#b3000000</drawable>
+ <drawable name="status_bar_notification_row_background_color">#ff000000</drawable>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ef9b8dd..6db5fc4 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -60,4 +60,7 @@
<!-- gap on either side of status bar notification icons -->
<dimen name="status_bar_icon_padding">0dp</dimen>
+
+ <!-- thickness (height) of dividers between each notification row -->
+ <dimen name="notification_divider_height">1dp</dimen>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
index 2d327c4..9749a1d 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
@@ -29,6 +29,7 @@
// should group this into a multi-property animation
private static final int OPEN_DURATION = 136;
private static final int CLOSE_DURATION = 250;
+ private static final int SCRIM_DURATION = 400;
private static final String TAG = RecentsPanelView.TAG;
private static final boolean DEBUG = RecentsPanelView.DEBUG;
@@ -71,12 +72,14 @@
posAnim.setInterpolator(appearing
? new android.view.animation.DecelerateInterpolator(2.5f)
: new android.view.animation.AccelerateInterpolator(2.5f));
+ posAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION);
Animator glowAnim = ObjectAnimator.ofFloat(mContentView, "alpha",
mContentView.getAlpha(), appearing ? 1.0f : 0.0f);
glowAnim.setInterpolator(appearing
? new android.view.animation.AccelerateInterpolator(1.0f)
: new android.view.animation.DecelerateInterpolator(1.0f));
+ glowAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION);
mContentAnim = new AnimatorSet();
final Builder builder = mContentAnim.play(glowAnim).with(posAnim);
@@ -84,9 +87,9 @@
if (background != null) {
Animator bgAnim = ObjectAnimator.ofInt(background,
"alpha", appearing ? 0 : 255, appearing ? 255 : 0);
+ bgAnim.setDuration(appearing ? SCRIM_DURATION : CLOSE_DURATION);
builder.with(bgAnim);
}
- mContentAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION);
mContentAnim.addListener(this);
if (mListener != null) {
mContentAnim.addListener(mListener);
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
index 36f1659..8da2db6 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
@@ -120,6 +120,9 @@
}
public void onBeginDrag(View v) {
+ // We do this so the underlying ScrollView knows that it won't get
+ // the chance to intercept events anymore
+ requestDisallowInterceptTouchEvent(true);
}
public View getChildAtPosition(MotionEvent ev) {
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index 8c03ef8..9cc2c29 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -39,7 +39,6 @@
import android.graphics.Shader.TileMode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
@@ -497,7 +496,7 @@
synchronized (ad) {
ad.mLabel = label;
ad.mIcon = icon;
- ad.setThumbnail(thumbs.mainThumbnail);
+ ad.setThumbnail(thumbs != null ? thumbs.mainThumbnail : null);
}
}
@@ -591,7 +590,7 @@
ActivityDescription ad = descriptions.get(i);
loadActivityDescription(ad, i);
long now = SystemClock.uptimeMillis();
- nextTime += 200;
+ nextTime += 150;
if (nextTime > now) {
try {
Thread.sleep(nextTime-now);
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
index 959328f..b1a30d9 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
@@ -135,6 +135,9 @@
}
public void onBeginDrag(View v) {
+ // We do this so the underlying ScrollView knows that it won't get
+ // the chance to intercept events anymore
+ requestDisallowInterceptTouchEvent(true);
}
public View getChildAtPosition(MotionEvent ev) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 76dec5e2..dd59667 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -181,7 +181,9 @@
private InputMethodsPanel mInputMethodsPanel;
private CompatModePanel mCompatModePanel;
- int mSystemUiVisibility = 0;
+ private int mSystemUiVisibility = 0;
+ // used to notify status bar for suppressing notification LED
+ private boolean mPanelSlightlyVisible;
public Context getContext() { return mContext; }
@@ -642,6 +644,7 @@
editor.putBoolean(Prefs.DO_NOT_DISTURB_PREF, false);
editor.apply();
animateCollapse();
+ visibilityChanged(false);
}
});
}
@@ -740,6 +743,7 @@
case MSG_HIDE_CHROME:
if (DEBUG) Slog.d(TAG, "showing shadows (lights out)");
animateCollapse();
+ visibilityChanged(false);
mBarContents.setVisibility(View.GONE);
mShadow.setVisibility(View.VISIBLE);
mSystemUiVisibility |= View.SYSTEM_UI_FLAG_LOW_PROFILE;
@@ -752,15 +756,6 @@
}
}
- private void notifyLightsChanged(boolean shown) {
- try {
- Slog.d(TAG, "lights " + (shown?"on":"out"));
- mWindowManager.statusBarVisibilityChanged(
- shown ? View.STATUS_BAR_VISIBLE : View.STATUS_BAR_HIDDEN);
- } catch (RemoteException ex) {
- }
- }
-
public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
if (DEBUG) Slog.d(TAG, "addIcon(" + slot + ") -> " + icon);
}
@@ -934,6 +929,7 @@
if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
Slog.i(TAG, "DISABLE_EXPAND: yes");
animateCollapse();
+ visibilityChanged(false);
}
}
if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
@@ -1036,6 +1032,24 @@
}
}
+ /**
+ * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
+ * This was added last-minute and is inconsistent with the way the rest of the notifications
+ * are handled, because the notification isn't really cancelled. The lights are just
+ * turned off. If any other notifications happen, the lights will turn back on. Steve says
+ * this is what he wants. (see bug 1131461)
+ */
+ void visibilityChanged(boolean visible) {
+ if (mPanelSlightlyVisible != visible) {
+ mPanelSlightlyVisible = visible;
+ try {
+ mBarService.onPanelRevealed();
+ } catch (RemoteException ex) {
+ // Won't fail unless the world has ended.
+ }
+ }
+ }
+
private void notifyUiVisibilityChanged() {
try {
mWindowManager.statusBarVisibilityChanged(mSystemUiVisibility);
@@ -1305,6 +1319,7 @@
// close the shade if it was open
animateCollapse();
+ visibilityChanged(false);
// If this click was on the intruder alert, hide that instead
// mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
@@ -1383,6 +1398,7 @@
// require a little more oomph once we're already in peekaboo mode
if (mVT.getYVelocity() < -mNotificationFlingVelocity) {
animateExpand();
+ visibilityChanged(true);
hilite(false);
mVT.recycle();
mVT = null;
@@ -1400,6 +1416,7 @@
// dragging off the bottom doesn't count
&& (int)event.getY() < v.getBottom()) {
animateExpand();
+ visibilityChanged(true);
v.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
v.playSoundEffect(SoundEffectConstants.CLICK);
}
@@ -1783,6 +1800,7 @@
// system process is dead if we're here.
}
animateCollapse();
+ visibilityChanged(false);
}
public void userActivity() {
diff --git a/policy/src/com/android/internal/policy/impl/AccountUnlockScreen.java b/policy/src/com/android/internal/policy/impl/AccountUnlockScreen.java
index 044cf4a..6ff9a60 100644
--- a/policy/src/com/android/internal/policy/impl/AccountUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/AccountUnlockScreen.java
@@ -311,12 +311,6 @@
mCheckingDialog.setCancelable(false);
mCheckingDialog.getWindow().setType(
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- if (!mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_sf_slowBlur)) {
- mCheckingDialog.getWindow().setFlags(
- WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
- WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
- }
}
return mCheckingDialog;
}
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index c47383a..ec31028 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -226,11 +226,6 @@
final AlertDialog dialog = ab.create();
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
- if (!mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_sf_slowBlur)) {
- dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
- WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
- }
dialog.setOnDismissListener(this);
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
index afa92f1..9629702 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
@@ -43,7 +43,7 @@
*
*/
class KeyguardStatusViewManager implements OnClickListener {
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
private static final String TAG = "KeyguardStatusView";
public static final int LOCK_ICON = 0; // R.drawable.ic_lock_idle_lock;
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
index 431f8e0..5661116 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
@@ -116,6 +116,7 @@
private static final int KEYGUARD_DONE_AUTHENTICATING = 11;
private static final int SET_HIDDEN = 12;
private static final int KEYGUARD_TIMEOUT = 13;
+ private static final int REPORT_SHOW_DONE = 14;
/**
* The default amount of time we stay awake (used for all key input)
@@ -238,6 +239,8 @@
private boolean mScreenOn = false;
+ private boolean mShowPending = false;
+
// last known state of the cellular connection
private String mPhoneState = TelephonyManager.EXTRA_STATE_IDLE;
@@ -306,7 +309,7 @@
synchronized (this) {
if (DEBUG) Log.d(TAG, "onSystemReady");
mSystemReady = true;
- doKeyguard();
+ doKeyguardLocked();
}
}
@@ -363,7 +366,7 @@
if (timeout <= 0) {
// Lock now
mSuppressNextLockSound = true;
- doKeyguard();
+ doKeyguardLocked();
} else {
// Lock in the future
long when = SystemClock.elapsedRealtime() + timeout;
@@ -379,7 +382,19 @@
} else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR) {
// Do not enable the keyguard if the prox sensor forced the screen off.
} else {
- doKeyguard();
+ if (!doKeyguardLocked() && why == WindowManagerPolicy.OFF_BECAUSE_OF_USER) {
+ // The user has explicitly turned off the screen, causing it
+ // to lock. We want to block here until the keyguard window
+ // has shown, so the power manager won't complete the screen
+ // off flow until that point, so we know it won't turn *on*
+ // the screen until this is done.
+ while (mShowPending) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
}
}
}
@@ -553,56 +568,58 @@
}
/**
- * Enable the keyguard if the settings are appropriate.
+ * Enable the keyguard if the settings are appropriate. Return true if all
+ * work that will happen is done; returns false if the caller can wait for
+ * the keyguard to be shown.
*/
- private void doKeyguard() {
- synchronized (this) {
- // if another app is disabling us, don't show
- if (!mExternallyEnabled) {
- if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
+ private boolean doKeyguardLocked() {
+ // if another app is disabling us, don't show
+ if (!mExternallyEnabled) {
+ if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
- // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes
- // for an occasional ugly flicker in this situation:
- // 1) receive a call with the screen on (no keyguard) or make a call
- // 2) screen times out
- // 3) user hits key to turn screen back on
- // instead, we reenable the keyguard when we know the screen is off and the call
- // ends (see the broadcast receiver below)
- // TODO: clean this up when we have better support at the window manager level
- // for apps that wish to be on top of the keyguard
- return;
- }
-
- // if the keyguard is already showing, don't bother
- if (mKeyguardViewManager.isShowing()) {
- if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
- return;
- }
-
- // if the setup wizard hasn't run yet, don't show
- final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim",
- false);
- final boolean provisioned = mUpdateMonitor.isDeviceProvisioned();
- final IccCard.State state = mUpdateMonitor.getSimState();
- final boolean lockedOrMissing = state.isPinLocked()
- || ((state == IccCard.State.ABSENT
- || state == IccCard.State.PERM_DISABLED)
- && requireSim);
-
- if (!lockedOrMissing && !provisioned) {
- if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"
- + " and the sim is not locked or missing");
- return;
- }
-
- if (mLockPatternUtils.isLockScreenDisabled()) {
- if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");
- return;
- }
-
- if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
- showLocked();
+ // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes
+ // for an occasional ugly flicker in this situation:
+ // 1) receive a call with the screen on (no keyguard) or make a call
+ // 2) screen times out
+ // 3) user hits key to turn screen back on
+ // instead, we reenable the keyguard when we know the screen is off and the call
+ // ends (see the broadcast receiver below)
+ // TODO: clean this up when we have better support at the window manager level
+ // for apps that wish to be on top of the keyguard
+ return true;
}
+
+ // if the keyguard is already showing, don't bother
+ if (mKeyguardViewManager.isShowing()) {
+ if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
+ return true;
+ }
+
+ // if the setup wizard hasn't run yet, don't show
+ final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim",
+ false);
+ final boolean provisioned = mUpdateMonitor.isDeviceProvisioned();
+ final IccCard.State state = mUpdateMonitor.getSimState();
+ final boolean lockedOrMissing = state.isPinLocked()
+ || ((state == IccCard.State.ABSENT
+ || state == IccCard.State.PERM_DISABLED)
+ && requireSim);
+
+ if (!lockedOrMissing && !provisioned) {
+ if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"
+ + " and the sim is not locked or missing");
+ return true;
+ }
+
+ if (mLockPatternUtils.isLockScreenDisabled()) {
+ if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");
+ return true;
+ }
+
+ if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
+ mShowPending = true;
+ showLocked();
+ return false;
}
/**
@@ -696,41 +713,49 @@
case ABSENT:
// only force lock screen in case of missing sim if user hasn't
// gone through setup wizard
- if (!mUpdateMonitor.isDeviceProvisioned()) {
- if (!isShowing()) {
- if (DEBUG) Log.d(TAG, "ICC_ABSENT isn't showing,"
- + " we need to show the keyguard since the "
- + "device isn't provisioned yet.");
- doKeyguard();
- } else {
- resetStateLocked();
+ synchronized (this) {
+ if (!mUpdateMonitor.isDeviceProvisioned()) {
+ if (!isShowing()) {
+ if (DEBUG) Log.d(TAG, "ICC_ABSENT isn't showing,"
+ + " we need to show the keyguard since the "
+ + "device isn't provisioned yet.");
+ doKeyguardLocked();
+ } else {
+ resetStateLocked();
+ }
}
}
break;
case PIN_REQUIRED:
case PUK_REQUIRED:
- if (!isShowing()) {
- if (DEBUG) Log.d(TAG, "INTENT_VALUE_ICC_LOCKED and keygaurd isn't showing, we need "
- + "to show the keyguard so the user can enter their sim pin");
- doKeyguard();
- } else {
- resetStateLocked();
+ synchronized (this) {
+ if (!isShowing()) {
+ if (DEBUG) Log.d(TAG, "INTENT_VALUE_ICC_LOCKED and keygaurd isn't showing, we need "
+ + "to show the keyguard so the user can enter their sim pin");
+ doKeyguardLocked();
+ } else {
+ resetStateLocked();
+ }
}
break;
case PERM_DISABLED:
- if (!isShowing()) {
- if (DEBUG) Log.d(TAG, "PERM_DISABLED and "
- + "keygaurd isn't showing.");
- doKeyguard();
- } else {
- if (DEBUG) Log.d(TAG, "PERM_DISABLED, resetStateLocked to"
- + "show permanently disabled message in lockscreen.");
- resetStateLocked();
+ synchronized (this) {
+ if (!isShowing()) {
+ if (DEBUG) Log.d(TAG, "PERM_DISABLED and "
+ + "keygaurd isn't showing.");
+ doKeyguardLocked();
+ } else {
+ if (DEBUG) Log.d(TAG, "PERM_DISABLED, resetStateLocked to"
+ + "show permanently disabled message in lockscreen.");
+ resetStateLocked();
+ }
}
break;
case READY:
- if (isShowing()) {
- resetStateLocked();
+ synchronized (this) {
+ if (isShowing()) {
+ resetStateLocked();
+ }
}
break;
}
@@ -751,27 +776,31 @@
if (DEBUG) Log.d(TAG, "received DELAYED_KEYGUARD_ACTION with seq = "
+ sequence + ", mDelayedShowingSequence = " + mDelayedShowingSequence);
- if (mDelayedShowingSequence == sequence) {
- // Don't play lockscreen SFX if the screen went off due to
- // timeout.
- mSuppressNextLockSound = true;
-
- doKeyguard();
+ synchronized (KeyguardViewMediator.this) {
+ if (mDelayedShowingSequence == sequence) {
+ // Don't play lockscreen SFX if the screen went off due to
+ // timeout.
+ mSuppressNextLockSound = true;
+
+ doKeyguardLocked();
+ }
}
} else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) {
mPhoneState = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
- if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState) // call ending
- && !mScreenOn // screen off
- && mExternallyEnabled) { // not disabled by any app
-
- // note: this is a way to gracefully reenable the keyguard when the call
- // ends and the screen is off without always reenabling the keyguard
- // each time the screen turns off while in call (and having an occasional ugly
- // flicker while turning back on the screen and disabling the keyguard again).
- if (DEBUG) Log.d(TAG, "screen is off and call ended, let's make sure the "
- + "keyguard is showing");
- doKeyguard();
+ synchronized (KeyguardViewMediator.this) {
+ if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState) // call ending
+ && !mScreenOn // screen off
+ && mExternallyEnabled) { // not disabled by any app
+
+ // note: this is a way to gracefully reenable the keyguard when the call
+ // ends and the screen is off without always reenabling the keyguard
+ // each time the screen turns off while in call (and having an occasional ugly
+ // flicker while turning back on the screen and disabling the keyguard again).
+ if (DEBUG) Log.d(TAG, "screen is off and call ended, let's make sure the "
+ + "keyguard is showing");
+ doKeyguardLocked();
+ }
}
}
}
@@ -962,7 +991,15 @@
handleSetHidden(msg.arg1 != 0);
break;
case KEYGUARD_TIMEOUT:
- doKeyguard();
+ synchronized (KeyguardViewMediator.this) {
+ doKeyguardLocked();
+ }
+ break;
+ case REPORT_SHOW_DONE:
+ synchronized (KeyguardViewMediator.this) {
+ mShowPending = false;
+ KeyguardViewMediator.this.notifyAll();
+ }
break;
}
}
@@ -1062,8 +1099,6 @@
if (DEBUG) Log.d(TAG, "handleShow");
if (!mSystemReady) return;
- playSounds(true);
-
mKeyguardViewManager.show();
mShowing = true;
adjustUserActivityLocked();
@@ -1072,7 +1107,17 @@
ActivityManagerNative.getDefault().closeSystemDialogs("lock");
} catch (RemoteException e) {
}
+
+ // Do this at the end to not slow down display of the keyguard.
+ playSounds(true);
+
mShowKeyguardWakeLock.release();
+
+ // We won't say the show is done yet because the view hierarchy
+ // still needs to do the traversal. Posting this message allows
+ // us to hold off until that is done.
+ Message msg = mHandler.obtainMessage(REPORT_SHOW_DONE);
+ mHandler.sendMessage(msg);
}
}
diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
index adcc9c0..2d90727 100644
--- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
+++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
@@ -760,12 +760,6 @@
.setNeutralButton(R.string.ok, null)
.create();
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- if (!mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_sf_slowBlur)) {
- dialog.getWindow().setFlags(
- WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
- WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
- }
dialog.show();
}
@@ -782,6 +776,7 @@
}
String message = mContext.getString(messageId, mUpdateMonitor.getFailedAttempts(),
timeoutInSeconds);
+
showDialog(null, message);
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 431a6bb..e2d6c5f 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -2512,8 +2512,15 @@
a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);
a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);
- if (getContext().getApplicationInfo().targetSdkVersion
- < android.os.Build.VERSION_CODES.HONEYCOMB) {
+ final Context context = getContext();
+ final int targetSdk = context.getApplicationInfo().targetSdkVersion;
+ final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;
+ final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
+ final boolean targetHcNeedsOptions = context.getResources().getBoolean(
+ com.android.internal.R.bool.target_honeycomb_needs_options_menu);
+ final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);
+
+ if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {
addFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY);
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 1d5fbc0a..be129a8 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -120,6 +120,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.LayoutParams.TYPE_POINTER;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
import android.view.WindowManagerImpl;
import android.view.WindowManagerPolicy;
import android.view.KeyCharacterMap.FallbackAction;
@@ -197,8 +198,9 @@
// things in here CAN NOT take focus, but are shown on top of everything else.
static final int SYSTEM_OVERLAY_LAYER = 20;
static final int SECURE_SYSTEM_OVERLAY_LAYER = 21;
+ static final int BOOT_PROGRESS_LAYER = 22;
// the (mouse) pointer layer
- static final int POINTER_LAYER = 22;
+ static final int POINTER_LAYER = 23;
static final int APPLICATION_MEDIA_SUBLAYER = -2;
static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1;
@@ -1095,6 +1097,8 @@
return POINTER_LAYER;
case TYPE_NAVIGATION_BAR:
return NAVIGATION_BAR_LAYER;
+ case TYPE_BOOT_PROGRESS:
+ return BOOT_PROGRESS_LAYER;
}
Log.e(TAG, "Unknown window type: " + type);
return APPLICATION_LAYER;
@@ -2797,13 +2801,26 @@
/** {@inheritDoc} */
public void screenTurnedOff(int why) {
EventLog.writeEvent(70000, 0);
- mKeyguardMediator.onScreenTurnedOff(why);
synchronized (mLock) {
mScreenOn = false;
+ }
+ mKeyguardMediator.onScreenTurnedOff(why);
+ synchronized (mLock) {
updateOrientationListenerLp();
updateLockScreenTimeout();
updateScreenSaverTimeoutLocked();
}
+ try {
+ mWindowManager.waitForAllDrawn();
+ } catch (RemoteException e) {
+ }
+ // Wait for one frame to give surface flinger time to do its
+ // compositing. Yes this is a hack, but I am really not up right now for
+ // implementing some mechanism to block until SF is done. :p
+ try {
+ Thread.sleep(20);
+ } catch (InterruptedException e) {
+ }
}
/** {@inheritDoc} */
@@ -3092,7 +3109,7 @@
mBootMsgDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mBootMsgDialog.setIndeterminate(true);
mBootMsgDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY);
+ WindowManager.LayoutParams.TYPE_BOOT_PROGRESS);
mBootMsgDialog.getWindow().addFlags(
WindowManager.LayoutParams.FLAG_DIM_BEHIND
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
diff --git a/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java b/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java
index f968bee..520d302 100644
--- a/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java
@@ -226,12 +226,6 @@
mSimUnlockProgressDialog.setCancelable(false);
mSimUnlockProgressDialog.getWindow().setType(
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- if (!mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_sf_slowBlur)) {
- mSimUnlockProgressDialog.getWindow().setFlags(
- WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
- WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
- }
}
return mSimUnlockProgressDialog;
}
diff --git a/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java b/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java
index 8bac969..1acf681 100644
--- a/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java
@@ -196,12 +196,6 @@
mSimUnlockProgressDialog.setCancelable(false);
mSimUnlockProgressDialog.getWindow().setType(
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- if (!mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_sf_slowBlur)) {
- mSimUnlockProgressDialog.getWindow().setFlags(
- WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
- WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
- }
}
return mSimUnlockProgressDialog;
}
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 941c9c8..744fa50 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -522,6 +522,11 @@
status_t AudioFlinger::setMasterVolume(float value)
{
+ status_t ret = initCheck();
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+
// check calling permissions
if (!settingsAllowed()) {
return PERMISSION_DENIED;
@@ -547,7 +552,10 @@
status_t AudioFlinger::setMode(int mode)
{
- status_t ret;
+ status_t ret = initCheck();
+ if (ret != NO_ERROR) {
+ return ret;
+ }
// check calling permissions
if (!settingsAllowed()) {
@@ -577,6 +585,11 @@
status_t AudioFlinger::setMicMute(bool state)
{
+ status_t ret = initCheck();
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+
// check calling permissions
if (!settingsAllowed()) {
return PERMISSION_DENIED;
@@ -584,13 +597,18 @@
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_HW_SET_MIC_MUTE;
- status_t ret = mPrimaryHardwareDev->set_mic_mute(mPrimaryHardwareDev, state);
+ ret = mPrimaryHardwareDev->set_mic_mute(mPrimaryHardwareDev, state);
mHardwareStatus = AUDIO_HW_IDLE;
return ret;
}
bool AudioFlinger::getMicMute() const
{
+ status_t ret = initCheck();
+ if (ret != NO_ERROR) {
+ return false;
+ }
+
bool state = AUDIO_MODE_INVALID;
mHardwareStatus = AUDIO_HW_GET_MIC_MUTE;
mPrimaryHardwareDev->get_mic_mute(mPrimaryHardwareDev, &state);
@@ -814,6 +832,11 @@
size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
{
+ status_t ret = initCheck();
+ if (ret != NO_ERROR) {
+ return 0;
+ }
+
return mPrimaryHardwareDev->get_input_buffer_size(mPrimaryHardwareDev, sampleRate, format, channelCount);
}
@@ -834,6 +857,11 @@
status_t AudioFlinger::setVoiceVolume(float value)
{
+ status_t ret = initCheck();
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+
// check calling permissions
if (!settingsAllowed()) {
return PERMISSION_DENIED;
@@ -841,7 +869,7 @@
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_SET_VOICE_VOLUME;
- status_t ret = mPrimaryHardwareDev->set_voice_volume(mPrimaryHardwareDev, value);
+ ret = mPrimaryHardwareDev->set_voice_volume(mPrimaryHardwareDev, value);
mHardwareStatus = AUDIO_HW_IDLE;
return ret;
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index f6ce44c..cf167ae 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -1831,8 +1831,8 @@
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
bool resumeWithAppendedMotionSample) {
#if DEBUG_DISPATCH_CYCLE
- LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, "
- "xOffset=%f, yOffset=%f, scaleFactor=%f"
+ LOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
+ "xOffset=%f, yOffset=%f, scaleFactor=%f, "
"pointerIds=0x%x, "
"resumeWithAppendedMotionSample=%s",
connection->getInputChannelName(), inputTarget->flags,
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 40c85fc..bfcf8e0 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -1220,6 +1220,9 @@
mBtnToolAirbrush = device->isKeyPressed(BTN_TOOL_AIRBRUSH);
mBtnToolMouse = device->isKeyPressed(BTN_TOOL_MOUSE);
mBtnToolLens = device->isKeyPressed(BTN_TOOL_LENS);
+ mBtnToolDoubleTap = device->isKeyPressed(BTN_TOOL_DOUBLETAP);
+ mBtnToolTripleTap = device->isKeyPressed(BTN_TOOL_TRIPLETAP);
+ mBtnToolQuadTap = device->isKeyPressed(BTN_TOOL_QUADTAP);
}
void TouchButtonAccumulator::clearButtons() {
@@ -1234,6 +1237,9 @@
mBtnToolAirbrush = 0;
mBtnToolMouse = 0;
mBtnToolLens = 0;
+ mBtnToolDoubleTap = 0;
+ mBtnToolTripleTap = 0;
+ mBtnToolQuadTap = 0;
}
void TouchButtonAccumulator::process(const RawEvent* rawEvent) {
@@ -1272,6 +1278,15 @@
case BTN_TOOL_LENS:
mBtnToolLens = rawEvent->value;
break;
+ case BTN_TOOL_DOUBLETAP:
+ mBtnToolDoubleTap = rawEvent->value;
+ break;
+ case BTN_TOOL_TRIPLETAP:
+ mBtnToolTripleTap = rawEvent->value;
+ break;
+ case BTN_TOOL_QUADTAP:
+ mBtnToolQuadTap = rawEvent->value;
+ break;
}
}
}
@@ -1297,7 +1312,7 @@
if (mBtnToolPen || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush) {
return AMOTION_EVENT_TOOL_TYPE_STYLUS;
}
- if (mBtnToolFinger) {
+ if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap) {
return AMOTION_EVENT_TOOL_TYPE_FINGER;
}
return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
@@ -1306,7 +1321,8 @@
bool TouchButtonAccumulator::isToolActive() const {
return mBtnTouch || mBtnToolFinger || mBtnToolPen || mBtnToolRubber
|| mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush
- || mBtnToolMouse || mBtnToolLens;
+ || mBtnToolMouse || mBtnToolLens
+ || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap;
}
bool TouchButtonAccumulator::isHovering() const {
@@ -2172,6 +2188,7 @@
}
nsecs_t downTime = mDownTime;
bool buttonsChanged = currentButtonState != lastButtonState;
+ bool buttonsPressed = currentButtonState & ~lastButtonState;
float deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale;
float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale;
@@ -2233,7 +2250,7 @@
// the device in your pocket.
// TODO: Use the input device configuration to control this behavior more finely.
uint32_t policyFlags = 0;
- if (getDevice()->isExternal()) {
+ if ((buttonsPressed || moved || scrolled) && getDevice()->isExternal()) {
policyFlags |= POLICY_FLAG_WAKE_DROPPED;
}
@@ -3329,9 +3346,12 @@
// Handle policy on initial down or hover events.
uint32_t policyFlags = 0;
- if (mLastRawPointerData.pointerCount == 0 && mCurrentRawPointerData.pointerCount != 0) {
+ bool initialDown = mLastRawPointerData.pointerCount == 0
+ && mCurrentRawPointerData.pointerCount != 0;
+ bool buttonsPressed = mCurrentButtonState & ~mLastButtonState;
+ if (initialDown || buttonsPressed) {
+ // If this is a touch screen, hide the pointer on an initial down.
if (mDeviceMode == DEVICE_MODE_DIRECT) {
- // If this is a touch screen, hide the pointer on an initial down.
getContext()->fadePointer();
}
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 76d77f1..bad96df 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -596,6 +596,9 @@
bool mBtnToolAirbrush;
bool mBtnToolMouse;
bool mBtnToolLens;
+ bool mBtnToolDoubleTap;
+ bool mBtnToolTripleTap;
+ bool mBtnToolQuadTap;
void clearButtons();
};
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index 4796958..32f948b 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -3377,8 +3377,32 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, motionArgs.pointerProperties[0].toolType);
- // finger
+ // double-tap
processKey(mapper, BTN_TOOL_LENS, 0);
+ processKey(mapper, BTN_TOOL_DOUBLETAP, 1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+ // triple-tap
+ processKey(mapper, BTN_TOOL_DOUBLETAP, 0);
+ processKey(mapper, BTN_TOOL_TRIPLETAP, 1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+ // quad-tap
+ processKey(mapper, BTN_TOOL_TRIPLETAP, 0);
+ processKey(mapper, BTN_TOOL_QUADTAP, 1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+ // finger
+ processKey(mapper, BTN_TOOL_QUADTAP, 0);
processKey(mapper, BTN_TOOL_FINGER, 1);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -4766,8 +4790,32 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, motionArgs.pointerProperties[0].toolType);
- // finger
+ // double-tap
processKey(mapper, BTN_TOOL_LENS, 0);
+ processKey(mapper, BTN_TOOL_DOUBLETAP, 1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+ // triple-tap
+ processKey(mapper, BTN_TOOL_DOUBLETAP, 0);
+ processKey(mapper, BTN_TOOL_TRIPLETAP, 1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+ // quad-tap
+ processKey(mapper, BTN_TOOL_TRIPLETAP, 0);
+ processKey(mapper, BTN_TOOL_QUADTAP, 1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+ // finger
+ processKey(mapper, BTN_TOOL_QUADTAP, 0);
processKey(mapper, BTN_TOOL_FINGER, 1);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 1341dd4..bfca851 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -72,7 +72,6 @@
import com.android.internal.telephony.Phone;
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
-
import com.google.android.collect.Lists;
import com.google.android.collect.Sets;
@@ -89,7 +88,6 @@
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
* @hide
@@ -251,6 +249,12 @@
private static final int EVENT_SEND_STICKY_BROADCAST_INTENT =
MAX_NETWORK_STATE_TRACKER_EVENT + 12;
+ /**
+ * Used internally to
+ * {@link NetworkStateTracker#setPolicyDataEnable(boolean)}.
+ */
+ private static final int EVENT_SET_POLICY_DATA_ENABLE = MAX_NETWORK_STATE_TRACKER_EVENT + 13;
+
private Handler mHandler;
// list of DeathRecipients used to make sure features are turned off when
@@ -1282,7 +1286,25 @@
if (VDBG) {
log(mNetTrackers[ConnectivityManager.TYPE_MOBILE].toString() + enabled);
}
- mNetTrackers[ConnectivityManager.TYPE_MOBILE].setDataEnable(enabled);
+ mNetTrackers[ConnectivityManager.TYPE_MOBILE].setUserDataEnable(enabled);
+ }
+ }
+
+ @Override
+ public void setPolicyDataEnable(int networkType, boolean enabled) {
+ // only someone like NPMS should only be calling us
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ mHandler.sendMessage(mHandler.obtainMessage(
+ EVENT_SET_POLICY_DATA_ENABLE, networkType, (enabled ? ENABLED : DISABLED)));
+ }
+
+ private void handleSetPolicyDataEnable(int networkType, boolean enabled) {
+ if (isNetworkTypeValid(networkType)) {
+ final NetworkStateTracker tracker = mNetTrackers[networkType];
+ if (tracker != null) {
+ tracker.setPolicyDataEnable(enabled);
+ }
}
}
@@ -2263,6 +2285,11 @@
sendStickyBroadcast(intent);
break;
}
+ case EVENT_SET_POLICY_DATA_ENABLE: {
+ final int networkType = msg.arg1;
+ final boolean enabled = msg.arg2 == ENABLED;
+ handleSetPolicyDataEnable(networkType, enabled);
+ }
}
}
}
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 6edb132..7a3a344 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -101,13 +101,8 @@
private Vibrator mVibrator = new Vibrator();
// for enabling and disabling notification pulse behavior
- private boolean mScreenOn = true;
private boolean mInCall = false;
private boolean mNotificationPulseEnabled;
- // This is true if we have received a new notification while the screen is off
- // (that is, if mLedNotification was set while the screen was off)
- // This is reset to false when the screen is turned on.
- private boolean mPendingPulseNotification;
private final ArrayList<NotificationRecord> mNotificationList =
new ArrayList<NotificationRecord>();
@@ -349,12 +344,6 @@
cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart);
}
}
- } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
- mScreenOn = true;
- updateNotificationPulse();
- } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
- mScreenOn = false;
- updateNotificationPulse();
} else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_OFFHOOK));
updateNotificationPulse();
@@ -1059,11 +1048,6 @@
// lock on mNotificationList
private void updateLightsLocked()
{
- // clear pending pulse notification if screen is on
- if (mScreenOn || mLedNotification == null) {
- mPendingPulseNotification = false;
- }
-
// handle notification lights
if (mLedNotification == null) {
// get next notification, if any
@@ -1071,14 +1055,10 @@
if (n > 0) {
mLedNotification = mLights.get(n-1);
}
- if (mLedNotification != null && !mScreenOn) {
- mPendingPulseNotification = true;
- }
}
- // we only flash if screen is off and persistent pulsing is enabled
- // and we are not currently in a call
- if (!mPendingPulseNotification || mScreenOn || mInCall) {
+ // Don't flash while we are in a call
+ if (mLedNotification == null || mInCall) {
mNotificationLight.turnOff();
} else {
int ledARGB = mLedNotification.notification.ledARGB;
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index d80a2cd..cbd986f 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -161,6 +161,7 @@
private int mStayOnConditions = 0;
private final int[] mBroadcastQueue = new int[] { -1, -1, -1 };
private final int[] mBroadcastWhy = new int[3];
+ private boolean mBroadcastingScreenOff = false;
private int mPartialCount = 0;
private int mPowerState;
// mScreenOffReason can be WindowManagerPolicy.OFF_BECAUSE_OF_USER,
@@ -1342,6 +1343,10 @@
mBroadcastWakeLock.release();
}
+ // The broadcast queue has changed; make sure the screen is on if it
+ // is now possible for it to be.
+ updateNativePowerStateLocked();
+
// Now send the message.
if (index >= 0) {
// Acquire the broadcast wake lock before changing the power
@@ -1370,6 +1375,9 @@
mBroadcastWhy[i] = mBroadcastWhy[i+1];
}
policy = getPolicyLocked();
+ if (value == 0) {
+ mBroadcastingScreenOff = true;
+ }
}
if (value == 1) {
mScreenOnStart = SystemClock.uptimeMillis();
@@ -1412,6 +1420,8 @@
synchronized (mLocks) {
EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3,
mBroadcastWakeLock.mCount);
+ mBroadcastingScreenOff = false;
+ updateNativePowerStateLocked();
mBroadcastWakeLock.release();
}
}
@@ -1442,6 +1452,10 @@
synchronized (mLocks) {
EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 0,
SystemClock.uptimeMillis() - mScreenOffStart, mBroadcastWakeLock.mCount);
+ synchronized (mLocks) {
+ mBroadcastingScreenOff = false;
+ updateNativePowerStateLocked();
+ }
mBroadcastWakeLock.release();
}
}
@@ -1768,6 +1782,22 @@
}
private void updateNativePowerStateLocked() {
+ if ((mPowerState & SCREEN_ON_BIT) != 0) {
+ // Don't turn screen on if we are currently reporting a screen off.
+ // This is to avoid letting the screen go on before things like the
+ // lock screen have been displayed due to it going off.
+ if (mBroadcastingScreenOff) {
+ // Currently broadcasting that the screen is off. Don't
+ // allow screen to go on until that is done.
+ return;
+ }
+ for (int i=0; i<mBroadcastQueue.length; i++) {
+ if (mBroadcastQueue[i] == 0) {
+ // A screen off is currently enqueued.
+ return;
+ }
+ }
+ }
nativeSetPowerState(
(mPowerState & SCREEN_ON_BIT) != 0,
(mPowerState & SCREEN_BRIGHT) == SCREEN_BRIGHT);
diff --git a/services/java/com/android/server/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java
index 90824a6..18ddabd 100644
--- a/services/java/com/android/server/TextServicesManagerService.java
+++ b/services/java/com/android/server/TextServicesManagerService.java
@@ -41,6 +41,7 @@
import android.text.TextUtils;
import android.util.Slog;
import android.view.textservice.SpellCheckerInfo;
+import android.view.textservice.SpellCheckerSubtype;
import java.io.IOException;
import java.util.ArrayList;
@@ -174,7 +175,7 @@
synchronized (mSpellCheckerMap) {
String curSpellCheckerId =
Settings.Secure.getString(mContext.getContentResolver(),
- Settings.Secure.SPELL_CHECKER_SERVICE);
+ Settings.Secure.SELECTED_SPELL_CHECKER);
if (DBG) {
Slog.w(TAG, "getCurrentSpellChecker: " + curSpellCheckerId);
}
@@ -185,6 +186,35 @@
}
}
+ // TODO: Save SpellCheckerSubtype by supported languages.
+ @Override
+ public SpellCheckerSubtype getCurrentSpellCheckerSubtype(String locale) {
+ synchronized (mSpellCheckerMap) {
+ final String subtypeHashCodeStr =
+ Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE);
+ if (DBG) {
+ Slog.w(TAG, "getCurrentSpellChecker: " + subtypeHashCodeStr);
+ }
+ final SpellCheckerInfo sci = getCurrentSpellChecker(null);
+ if (sci.getSubtypeCount() == 0) {
+ return null;
+ }
+ if (TextUtils.isEmpty(subtypeHashCodeStr)) {
+ // Return the first Subtype if there is no settings for the current subtype.
+ return sci.getSubtypeAt(0);
+ }
+ final int hashCode = Integer.valueOf(subtypeHashCodeStr);
+ for (int i = 0; i < sci.getSubtypeCount(); ++i) {
+ final SpellCheckerSubtype scs = sci.getSubtypeAt(i);
+ if (scs.hashCode() == hashCode) {
+ return scs;
+ }
+ }
+ return sci.getSubtypeAt(0);
+ }
+ }
+
@Override
public void getSpellCheckerService(String sciId, String locale,
ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener,
@@ -301,7 +331,7 @@
}
@Override
- public void setCurrentSpellChecker(String sciId) {
+ public void setCurrentSpellChecker(String locale, String sciId) {
synchronized(mSpellCheckerMap) {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.WRITE_SECURE_SETTINGS)
@@ -314,6 +344,20 @@
}
}
+ @Override
+ public void setCurrentSpellCheckerSubtype(String locale, int hashCode) {
+ synchronized(mSpellCheckerMap) {
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "Requires permission "
+ + android.Manifest.permission.WRITE_SECURE_SETTINGS);
+ }
+ setCurrentSpellCheckerLocked(hashCode);
+ }
+ }
+
private void setCurrentSpellCheckerLocked(String sciId) {
if (DBG) {
Slog.w(TAG, "setCurrentSpellChecker: " + sciId);
@@ -322,7 +366,32 @@
final long ident = Binder.clearCallingIdentity();
try {
Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.SPELL_CHECKER_SERVICE, sciId);
+ Settings.Secure.SELECTED_SPELL_CHECKER, sciId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void setCurrentSpellCheckerLocked(int hashCode) {
+ if (DBG) {
+ Slog.w(TAG, "setCurrentSpellCheckerSubtype: " + hashCode);
+ }
+ final SpellCheckerInfo sci = getCurrentSpellChecker(null);
+ if (sci == null) return;
+ boolean found = false;
+ for (int i = 0; i < sci.getSubtypeCount(); ++i) {
+ if(sci.getSubtypeAt(i).hashCode() == hashCode) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return;
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ Settings.Secure.putString(mContext.getContentResolver(),
+ Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, String.valueOf(hashCode));
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index b817598..7232a94 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -6564,7 +6564,7 @@
i--;
}
}
-
+
for (int i=0; i<ris.size(); i++) {
ActivityInfo ai = ris.get(i).activityInfo;
ComponentName comp = new ComponentName(ai.packageName, ai.name);
@@ -9026,6 +9026,7 @@
final static class MemItem {
final String label;
final long pss;
+ ArrayList<MemItem> subitems;
public MemItem(String _label, long _pss) {
label = _label;
@@ -9051,7 +9052,10 @@
for (int i=0; i<items.size(); i++) {
MemItem mi = items.get(i);
- pw.print(prefix); pw.printf("%8d Kb: ", mi.pss); pw.println(mi.label);
+ pw.print(prefix); pw.printf("%7d Kb: ", mi.pss); pw.println(mi.label);
+ if (mi.subitems != null) {
+ dumpMemItems(pw, prefix + " ", mi.subitems, true);
+ }
}
}
@@ -9119,6 +9123,7 @@
"Backup", "Services", "Home", "Background"
};
long oomPss[] = new long[oomLabel.length];
+ ArrayList<MemItem>[] oomProcs = (ArrayList<MemItem>[])new ArrayList[oomLabel.length];
long totalPss = 0;
@@ -9147,7 +9152,9 @@
if (!isCheckinRequest && mi != null) {
long myTotalPss = mi.getTotalPss();
totalPss += myTotalPss;
- procMems.add(new MemItem(r.processName + " (pid " + r.pid + ")", myTotalPss));
+ MemItem pssItem = new MemItem(r.processName + " (pid " + r.pid + ")",
+ myTotalPss);
+ procMems.add(pssItem);
nativePss += mi.nativePss;
dalvikPss += mi.dalvikPss;
@@ -9161,6 +9168,10 @@
for (int oomIndex=0; oomIndex<oomPss.length; oomIndex++) {
if (r.setAdj <= oomAdj[oomIndex] || oomIndex == (oomPss.length-1)) {
oomPss[oomIndex] += myTotalPss;
+ if (oomProcs[oomIndex] == null) {
+ oomProcs[oomIndex] = new ArrayList<MemItem>();
+ }
+ oomProcs[oomIndex].add(pssItem);
break;
}
}
@@ -9181,7 +9192,9 @@
ArrayList<MemItem> oomMems = new ArrayList<MemItem>();
for (int j=0; j<oomPss.length; j++) {
if (oomPss[j] != 0) {
- oomMems.add(new MemItem(oomLabel[j], oomPss[j]));
+ MemItem item = new MemItem(oomLabel[j], oomPss[j]);
+ item.subitems = oomProcs[j];
+ oomMems.add(item);
}
}
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 6f0779f..4ad0f45 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -3163,7 +3163,7 @@
//Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
if (mMainStack) {
- if (!mService.mBooted && !fromTimeout) {
+ if (!mService.mBooted) {
mService.mBooted = true;
enableScreen = true;
}
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index e61a7f4..87129ea 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -116,6 +116,8 @@
if (!askedCompatMode) {
pw.print(prefix); pw.print("askedCompatMode="); pw.println(askedCompatMode);
}
+ pw.print(prefix); pw.print("lastThumbnail="); pw.print(lastThumbnail);
+ pw.print(" lastDescription="); pw.println(lastDescription);
pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime);
pw.print(" (inactive for ");
pw.print((getInactiveDuration()/1000)); pw.println("s)");
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index 6b65e07..55e0678 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -388,6 +388,7 @@
private final VpnConfig mConfig;
private final String[] mDaemons;
private final String[][] mArguments;
+ private final LocalSocket[] mSockets;
private final String mOuterInterface;
private final LegacyVpnInfo mInfo;
@@ -398,6 +399,7 @@
mConfig = config;
mDaemons = new String[] {"racoon", "mtpd"};
mArguments = new String[][] {racoon, mtpd};
+ mSockets = new LocalSocket[mDaemons.length];
mInfo = new LegacyVpnInfo();
// This is the interface which VPN is running on.
@@ -416,10 +418,14 @@
}
public void exit() {
- // We assume that everything is reset after the daemons die.
+ // We assume that everything is reset after stopping the daemons.
interrupt();
- for (String daemon : mDaemons) {
- SystemProperties.set("ctl.stop", daemon);
+ for (LocalSocket socket : mSockets) {
+ try {
+ socket.close();
+ } catch (Exception e) {
+ // ignore
+ }
}
}
@@ -462,15 +468,10 @@
checkpoint(false);
mInfo.state = LegacyVpnInfo.STATE_INITIALIZING;
- // First stop the daemons.
- for (String daemon : mDaemons) {
- SystemProperties.set("ctl.stop", daemon);
- }
-
// Wait for the daemons to stop.
for (String daemon : mDaemons) {
String key = "init.svc." + daemon;
- while (!"stopped".equals(SystemProperties.get(key))) {
+ while (!"stopped".equals(SystemProperties.get(key, "stopped"))) {
checkpoint(true);
}
}
@@ -511,27 +512,27 @@
}
// Create the control socket.
- LocalSocket socket = new LocalSocket();
+ mSockets[i] = new LocalSocket();
LocalSocketAddress address = new LocalSocketAddress(
daemon, LocalSocketAddress.Namespace.RESERVED);
// Wait for the socket to connect.
while (true) {
try {
- socket.connect(address);
+ mSockets[i].connect(address);
break;
} catch (Exception e) {
// ignore
}
checkpoint(true);
}
- socket.setSoTimeout(500);
+ mSockets[i].setSoTimeout(500);
// Send over the arguments.
- OutputStream out = socket.getOutputStream();
+ OutputStream out = mSockets[i].getOutputStream();
for (String argument : arguments) {
byte[] bytes = argument.getBytes(Charsets.UTF_8);
- if (bytes.length > 0xFFFF) {
+ if (bytes.length >= 0xFFFF) {
throw new IllegalArgumentException("Argument is too large");
}
out.write(bytes.length >> 8);
@@ -539,11 +540,12 @@
out.write(bytes);
checkpoint(false);
}
+ out.write(0xFF);
+ out.write(0xFF);
out.flush();
- socket.shutdownOutput();
// Wait for End-of-File.
- InputStream in = socket.getInputStream();
+ InputStream in = mSockets[i].getInputStream();
while (true) {
try {
if (in.read() == -1) {
@@ -554,7 +556,6 @@
}
checkpoint(true);
}
- socket.close();
}
// Wait for the daemons to create the new state.
@@ -631,6 +632,13 @@
Log.i(TAG, "Aborting", e);
exit();
} finally {
+ // Kill the daemons if they fail to stop.
+ if (mInfo.state == LegacyVpnInfo.STATE_INITIALIZING) {
+ for (String daemon : mDaemons) {
+ SystemProperties.set("ctl.stop", daemon);
+ }
+ }
+
// Do not leave an unstable state.
if (mInfo.state == LegacyVpnInfo.STATE_INITIALIZING ||
mInfo.state == LegacyVpnInfo.STATE_CONNECTING) {
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 14d9665..84880f9 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -27,7 +27,10 @@
import static android.content.Intent.ACTION_UID_REMOVED;
import static android.content.Intent.EXTRA_UID;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.ConnectivityManager.TYPE_WIMAX;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.SNOOZE_NEVER;
import static android.net.NetworkPolicy.WARNING_DISABLED;
@@ -40,8 +43,11 @@
import static android.net.NetworkPolicyManager.dumpPolicy;
import static android.net.NetworkPolicyManager.dumpRules;
import static android.net.NetworkPolicyManager.isUidValidForPolicy;
+import static android.net.NetworkTemplate.MATCH_ETHERNET;
import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER;
import static android.net.NetworkTemplate.MATCH_MOBILE_4G;
+import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
+import static android.net.NetworkTemplate.MATCH_WIFI;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static com.android.internal.util.Preconditions.checkNotNull;
@@ -104,6 +110,7 @@
import com.android.internal.R;
import com.android.internal.os.AtomicFile;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.Objects;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
import com.google.android.collect.Sets;
@@ -129,8 +136,8 @@
import libcore.io.IoUtils;
/**
- * Service that maintains low-level network policy rules and collects usage
- * statistics to drive those rules.
+ * Service that maintains low-level network policy rules, using
+ * {@link NetworkStatsService} statistics to drive those rules.
* <p>
* Derives active rules by combining a given policy with other system status,
* and delivers to listeners, such as {@link ConnectivityManager}, for
@@ -195,6 +202,8 @@
private volatile boolean mScreenOn;
private volatile boolean mRestrictBackground;
+ private final boolean mSuppressDefaultPolicy;
+
/** Defined network policies. */
private HashMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = Maps.newHashMap();
/** Currently active network rules for ifaces. */
@@ -210,6 +219,11 @@
/** Set of over-limit templates that have been notified. */
private HashSet<NetworkTemplate> mOverLimitNotified = Sets.newHashSet();
+ /** Set of currently active {@link Notification} tags. */
+ private HashSet<String> mActiveNotifs = Sets.newHashSet();
+ /** Current values from {@link #setPolicyDataEnable(int, boolean)}. */
+ private SparseBooleanArray mActiveNetworkEnabled = new SparseBooleanArray();
+
/** Foreground at both UID and PID granularity. */
private SparseBooleanArray mUidForeground = new SparseBooleanArray();
private SparseArray<SparseBooleanArray> mUidPidForeground = new SparseArray<
@@ -232,7 +246,7 @@
IPowerManager powerManager, INetworkStatsService networkStats,
INetworkManagementService networkManagement) {
this(context, activityManager, powerManager, networkStats, networkManagement,
- NtpTrustedTime.getInstance(context), getSystemDir());
+ NtpTrustedTime.getInstance(context), getSystemDir(), false);
}
private static File getSystemDir() {
@@ -241,8 +255,8 @@
public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
IPowerManager powerManager, INetworkStatsService networkStats,
- INetworkManagementService networkManagement,
- TrustedTime time, File systemDir) {
+ INetworkManagementService networkManagement, TrustedTime time, File systemDir,
+ boolean suppressDefaultPolicy) {
mContext = checkNotNull(context, "missing context");
mActivityManager = checkNotNull(activityManager, "missing activityManager");
mPowerManager = checkNotNull(powerManager, "missing powerManager");
@@ -254,6 +268,8 @@
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper(), mHandlerCallback);
+ mSuppressDefaultPolicy = suppressDefaultPolicy;
+
mPolicyFile = new AtomicFile(new File(systemDir, "netpolicy.xml"));
}
@@ -408,6 +424,7 @@
// READ_NETWORK_USAGE_HISTORY permission above.
synchronized (mRulesLock) {
+ updateNetworkEnabledLocked();
updateNotificationsLocked();
}
}
@@ -446,6 +463,7 @@
Slog.w(TAG, "problem updating network stats");
}
+ updateNetworkEnabledLocked();
updateNotificationsLocked();
}
}
@@ -459,74 +477,70 @@
private void updateNotificationsLocked() {
if (LOGV) Slog.v(TAG, "updateNotificationsLocked()");
- // try refreshing time source when stale
- if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE) {
- mTime.forceRefresh();
- }
-
- final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
- : System.currentTimeMillis();
+ // keep track of previously active notifications
+ final HashSet<String> beforeNotifs = Sets.newHashSet();
+ beforeNotifs.addAll(mActiveNotifs);
+ mActiveNotifs.clear();
// TODO: when switching to kernel notifications, compute next future
// cycle boundary to recompute notifications.
// examine stats for each active policy
- for (NetworkPolicy policy : mNetworkRules.keySet()) {
+ final long currentTime = currentTimeMillis(true);
+ for (NetworkPolicy policy : mNetworkPolicy.values()) {
+ // ignore policies that aren't relevant to user
+ if (!isTemplateRelevant(policy.template)) continue;
+
final long start = computeLastCycleBoundary(currentTime, policy);
final long end = currentTime;
- final long totalBytes;
- try {
- final NetworkStats stats = mNetworkStats.getSummaryForNetwork(
- policy.template, start, end);
- final NetworkStats.Entry entry = stats.getValues(0, null);
- totalBytes = entry.rxBytes + entry.txBytes;
- } catch (RemoteException e) {
- Slog.w(TAG, "problem reading summary for template " + policy.template);
- continue;
- }
+ final long totalBytes = getTotalBytes(policy.template, start, end);
+ if (totalBytes == UNKNOWN_BYTES) continue;
if (policy.limitBytes != LIMIT_DISABLED && totalBytes >= policy.limitBytes) {
- cancelNotification(policy, TYPE_WARNING);
-
if (policy.lastSnooze >= start) {
- cancelNotification(policy, TYPE_LIMIT);
enqueueNotification(policy, TYPE_LIMIT_SNOOZED, totalBytes);
} else {
- cancelNotification(policy, TYPE_LIMIT_SNOOZED);
enqueueNotification(policy, TYPE_LIMIT, totalBytes);
notifyOverLimitLocked(policy.template);
}
} else {
- cancelNotification(policy, TYPE_LIMIT);
- cancelNotification(policy, TYPE_LIMIT_SNOOZED);
notifyUnderLimitLocked(policy.template);
if (policy.warningBytes != WARNING_DISABLED && totalBytes >= policy.warningBytes) {
enqueueNotification(policy, TYPE_WARNING, totalBytes);
- } else {
- cancelNotification(policy, TYPE_WARNING);
}
}
}
- // clear notifications for non-active policies
- for (NetworkPolicy policy : mNetworkPolicy.values()) {
- if (!mNetworkRules.containsKey(policy)) {
- cancelNotification(policy, TYPE_WARNING);
- cancelNotification(policy, TYPE_LIMIT);
- cancelNotification(policy, TYPE_LIMIT_SNOOZED);
- notifyUnderLimitLocked(policy.template);
- }
- }
-
// ongoing notification when restricting background data
if (mRestrictBackground) {
enqueueRestrictedNotification(TAG_ALLOW_BACKGROUND);
- } else {
- cancelNotification(TAG_ALLOW_BACKGROUND);
}
+
+ // cancel stale notifications that we didn't renew above
+ for (String tag : beforeNotifs) {
+ if (!mActiveNotifs.contains(tag)) {
+ cancelNotification(tag);
+ }
+ }
+ }
+
+ /**
+ * Test if given {@link NetworkTemplate} is relevant to user based on
+ * current device state, such as when {@link #getActiveSubscriberId()}
+ * matches. This is regardless of data connection status.
+ */
+ private boolean isTemplateRelevant(NetworkTemplate template) {
+ switch (template.getMatchRule()) {
+ case MATCH_MOBILE_3G_LOWER:
+ case MATCH_MOBILE_4G:
+ case MATCH_MOBILE_ALL:
+ // mobile templates are relevant when subscriberid is active
+ return Objects.equal(getActiveSubscriberId(), template.getSubscriberId());
+ }
+ return true;
}
/**
@@ -590,9 +604,15 @@
case MATCH_MOBILE_4G:
title = res.getText(R.string.data_usage_4g_limit_title);
break;
- default:
+ case MATCH_MOBILE_ALL:
title = res.getText(R.string.data_usage_mobile_limit_title);
break;
+ case MATCH_WIFI:
+ title = res.getText(R.string.data_usage_wifi_limit_title);
+ break;
+ default:
+ title = null;
+ break;
}
builder.setSmallIcon(com.android.internal.R.drawable.ic_menu_block);
@@ -618,9 +638,15 @@
case MATCH_MOBILE_4G:
title = res.getText(R.string.data_usage_4g_limit_snoozed_title);
break;
- default:
+ case MATCH_MOBILE_ALL:
title = res.getText(R.string.data_usage_mobile_limit_snoozed_title);
break;
+ case MATCH_WIFI:
+ title = res.getText(R.string.data_usage_wifi_limit_snoozed_title);
+ break;
+ default:
+ title = null;
+ break;
}
builder.setSmallIcon(R.drawable.ic_menu_info_details);
@@ -641,6 +667,7 @@
final int[] idReceived = new int[1];
mNotifManager.enqueueNotificationWithTag(
packageName, tag, 0x0, builder.getNotification(), idReceived);
+ mActiveNotifs.add(tag);
} catch (RemoteException e) {
Slog.w(TAG, "problem during enqueueNotification: " + e);
}
@@ -674,19 +701,12 @@
final int[] idReceived = new int[1];
mNotifManager.enqueueNotificationWithTag(packageName, tag,
0x0, builder.getNotification(), idReceived);
+ mActiveNotifs.add(tag);
} catch (RemoteException e) {
Slog.w(TAG, "problem during enqueueNotification: " + e);
}
}
- /**
- * Cancel any notification for combined {@link NetworkPolicy} and specific
- * type, like {@link #TYPE_LIMIT}.
- */
- private void cancelNotification(NetworkPolicy policy, int type) {
- cancelNotification(buildNotificationTag(policy, type));
- }
-
private void cancelNotification(String tag) {
// TODO: move to NotificationManager once we can mock it
try {
@@ -709,6 +729,7 @@
// permission above.
synchronized (mRulesLock) {
ensureActiveMobilePolicyLocked();
+ updateNetworkEnabledLocked();
updateNetworkRulesLocked();
updateNotificationsLocked();
}
@@ -716,6 +737,65 @@
};
/**
+ * Proactively control network data connections when they exceed
+ * {@link NetworkPolicy#limitBytes}.
+ */
+ private void updateNetworkEnabledLocked() {
+ if (LOGV) Slog.v(TAG, "updateNetworkEnabledLocked()");
+
+ // TODO: reset any policy-disabled networks when any policy is removed
+ // completely, which is currently rare case.
+
+ final long currentTime = currentTimeMillis(true);
+ for (NetworkPolicy policy : mNetworkPolicy.values()) {
+ // shortcut when policy has no limit
+ if (policy.limitBytes == LIMIT_DISABLED) {
+ setNetworkTemplateEnabled(policy.template, true);
+ continue;
+ }
+
+ final long start = computeLastCycleBoundary(currentTime, policy);
+ final long end = currentTime;
+
+ final long totalBytes = getTotalBytes(policy.template, start, end);
+ if (totalBytes == UNKNOWN_BYTES) continue;
+
+ // disable data connection when over limit and not snoozed
+ final boolean overLimit = policy.limitBytes != LIMIT_DISABLED
+ && totalBytes > policy.limitBytes && policy.lastSnooze < start;
+ setNetworkTemplateEnabled(policy.template, !overLimit);
+ }
+ }
+
+ /**
+ * Control {@link IConnectivityManager#setPolicyDataEnable(int, boolean)}
+ * for the given {@link NetworkTemplate}.
+ */
+ private void setNetworkTemplateEnabled(NetworkTemplate template, boolean enabled) {
+ if (LOGD) Slog.d(TAG, "setting template=" + template + " enabled=" + enabled);
+ switch (template.getMatchRule()) {
+ case MATCH_MOBILE_3G_LOWER:
+ case MATCH_MOBILE_4G:
+ case MATCH_MOBILE_ALL:
+ // TODO: offer more granular control over radio states once
+ // 4965893 is available.
+ if (Objects.equal(getActiveSubscriberId(), template.getSubscriberId())) {
+ setPolicyDataEnable(TYPE_MOBILE, enabled);
+ setPolicyDataEnable(TYPE_WIMAX, enabled);
+ }
+ break;
+ case MATCH_WIFI:
+ setPolicyDataEnable(TYPE_WIFI, enabled);
+ break;
+ case MATCH_ETHERNET:
+ setPolicyDataEnable(TYPE_ETHERNET, enabled);
+ break;
+ default:
+ throw new IllegalArgumentException("unexpected template");
+ }
+ }
+
+ /**
* Examine all connected {@link NetworkState}, looking for
* {@link NetworkPolicy} that need to be enforced. When matches found, set
* remaining quota based on usage cycle and historical stats.
@@ -763,34 +843,19 @@
}
}
- // try refreshing time source when stale
- if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE) {
- mTime.forceRefresh();
- }
-
- final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
- : System.currentTimeMillis();
-
final HashSet<String> newMeteredIfaces = Sets.newHashSet();
// apply each policy that we found ifaces for; compute remaining data
// based on current cycle and historical stats, and push to kernel.
+ final long currentTime = currentTimeMillis(true);
for (NetworkPolicy policy : mNetworkRules.keySet()) {
final String[] ifaces = mNetworkRules.get(policy);
final long start = computeLastCycleBoundary(currentTime, policy);
final long end = currentTime;
- final NetworkStats stats;
- final long total;
- try {
- stats = mNetworkStats.getSummaryForNetwork(policy.template, start, end);
- final NetworkStats.Entry entry = stats.getValues(0, null);
- total = entry.rxBytes + entry.txBytes;
- } catch (RemoteException e) {
- Slog.w(TAG, "problem reading summary for template " + policy.template);
- continue;
- }
+ final long totalBytes = getTotalBytes(policy.template, start, end);
+ if (totalBytes == UNKNOWN_BYTES) continue;
if (LOGD) {
Slog.d(TAG, "applying policy " + policy.toString() + " to ifaces "
@@ -798,11 +863,18 @@
}
final boolean hasLimit = policy.limitBytes != LIMIT_DISABLED;
- final boolean hasWarning = policy.warningBytes != WARNING_DISABLED;
-
if (hasLimit) {
- // remaining "quota" is based on usage in current cycle
- final long quotaBytes = Math.max(0, policy.limitBytes - total);
+ final long quotaBytes;
+ if (policy.lastSnooze >= start) {
+ // snoozing past quota, but we still need to restrict apps,
+ // so push really high quota.
+ quotaBytes = Long.MAX_VALUE;
+ } else {
+ // remaining "quota" bytes are based on total usage in
+ // current cycle. kernel doesn't like 0-byte rules, so we
+ // set 1-byte quota and disable the radio later.
+ quotaBytes = Math.max(1, policy.limitBytes - totalBytes);
+ }
if (ifaces.length > 1) {
// TODO: switch to shared quota once NMS supports
@@ -811,10 +883,8 @@
for (String iface : ifaces) {
removeInterfaceQuota(iface);
- if (quotaBytes > 0) {
- setInterfaceQuota(iface, quotaBytes);
- newMeteredIfaces.add(iface);
- }
+ setInterfaceQuota(iface, quotaBytes);
+ newMeteredIfaces.add(iface);
}
}
}
@@ -837,6 +907,8 @@
*/
private void ensureActiveMobilePolicyLocked() {
if (LOGV) Slog.v(TAG, "ensureActiveMobilePolicyLocked()");
+ if (mSuppressDefaultPolicy) return;
+
final String subscriberId = getActiveSubscriberId();
final NetworkIdentity probeIdent = new NetworkIdentity(
TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, false);
@@ -1073,6 +1145,7 @@
mNetworkPolicy.put(policy.template, policy);
}
+ updateNetworkEnabledLocked();
updateNetworkRulesLocked();
updateNotificationsLocked();
writePolicyLocked();
@@ -1093,14 +1166,7 @@
public void snoozePolicy(NetworkTemplate template) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- // try refreshing time source when stale
- if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE) {
- mTime.forceRefresh();
- }
-
- final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
- : System.currentTimeMillis();
-
+ final long currentTime = currentTimeMillis(true);
synchronized (mRulesLock) {
// find and snooze local policy that matches
final NetworkPolicy policy = mNetworkPolicy.get(template);
@@ -1110,6 +1176,7 @@
policy.lastSnooze = currentTime;
+ updateNetworkEnabledLocked();
updateNetworkRulesLocked();
updateNotificationsLocked();
writePolicyLocked();
@@ -1173,22 +1240,14 @@
return null;
}
- final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
- : System.currentTimeMillis();
+ final long currentTime = currentTimeMillis(false);
final long start = computeLastCycleBoundary(currentTime, policy);
final long end = currentTime;
// find total bytes used under policy
- long totalBytes = 0;
- try {
- final NetworkStats stats = mNetworkStats.getSummaryForNetwork(
- policy.template, start, end);
- final NetworkStats.Entry entry = stats.getValues(0, null);
- totalBytes = entry.rxBytes + entry.txBytes;
- } catch (RemoteException e) {
- Slog.w(TAG, "problem reading summary for template " + policy.template);
- }
+ final long totalBytes = getTotalBytes(policy.template, start, end);
+ if (totalBytes == UNKNOWN_BYTES) return null;
// report soft and hard limits under policy
final long softLimitBytes = policy.warningBytes != WARNING_DISABLED ? policy.warningBytes
@@ -1481,12 +1540,54 @@
}
}
+ /**
+ * Control {@link IConnectivityManager#setPolicyDataEnable(int, boolean)},
+ * dispatching only when actually changed.
+ */
+ private void setPolicyDataEnable(int networkType, boolean enabled) {
+ synchronized (mActiveNetworkEnabled) {
+ final boolean prevEnabled = mActiveNetworkEnabled.get(networkType, true);
+ if (prevEnabled == enabled) return;
+
+ try {
+ mConnManager.setPolicyDataEnable(networkType, enabled);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "problem setting network enabled", e);
+ }
+
+ mActiveNetworkEnabled.put(networkType, enabled);
+ }
+ }
+
private String getActiveSubscriberId() {
final TelephonyManager telephony = (TelephonyManager) mContext.getSystemService(
Context.TELEPHONY_SERVICE);
return telephony.getSubscriberId();
}
+ private static final long UNKNOWN_BYTES = -1;
+
+ private long getTotalBytes(NetworkTemplate template, long start, long end) {
+ try {
+ final NetworkStats stats = mNetworkStats.getSummaryForNetwork(
+ template, start, end);
+ final NetworkStats.Entry entry = stats.getValues(0, null);
+ return entry.rxBytes + entry.txBytes;
+ } catch (RemoteException e) {
+ Slog.w(TAG, "problem reading summary for template " + template);
+ return UNKNOWN_BYTES;
+ }
+ }
+
+ private long currentTimeMillis(boolean allowRefresh) {
+ // try refreshing time source when stale
+ if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE && allowRefresh) {
+ mTime.forceRefresh();
+ }
+
+ return mTime.hasCache() ? mTime.currentTimeMillis() : System.currentTimeMillis();
+ }
+
private static Intent buildAllowBackgroundDataIntent() {
return new Intent(ACTION_ALLOW_BACKGROUND);
}
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index a01c975..8f51466 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -568,12 +568,9 @@
notification.sound = null;
notification.vibrate = null;
- Intent intent = new Intent(
- Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- intent.setComponent(new ComponentName("com.android.settings",
- "com.android.settings.UsbSettings"));
+ Intent intent = Intent.makeRestartActivityTask(
+ new ComponentName("com.android.settings",
+ "com.android.settings.UsbSettings"));
PendingIntent pi = PendingIntent.getActivity(mContext, 0,
intent, 0);
notification.setLatestEventInfo(mContext, title, message, pi);
@@ -604,12 +601,9 @@
notification.sound = null;
notification.vibrate = null;
- Intent intent = new Intent(
- Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- intent.setComponent(new ComponentName("com.android.settings",
- "com.android.settings.DevelopmentSettings"));
+ Intent intent = Intent.makeRestartActivityTask(
+ new ComponentName("com.android.settings",
+ "com.android.settings.DevelopmentSettings"));
PendingIntent pi = PendingIntent.getActivity(mContext, 0,
intent, 0);
notification.setLatestEventInfo(mContext, title, message, pi);
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index dc5555e..e258b1a 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -50,11 +50,9 @@
import android.Manifest;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
-import android.app.ProgressDialog;
import android.app.StatusBarManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -164,6 +162,8 @@
static final boolean DEBUG_REORDER = false;
static final boolean DEBUG_WALLPAPER = false;
static final boolean DEBUG_DRAG = false;
+ static final boolean DEBUG_SCREEN_ON = false;
+ static final boolean DEBUG_SCREENSHOT = false;
static final boolean SHOW_SURFACE_ALLOC = false;
static final boolean SHOW_TRANSACTIONS = false;
static final boolean HIDE_STACK_CRAWLS = true;
@@ -408,6 +408,7 @@
boolean mSafeMode;
boolean mDisplayEnabled = false;
boolean mSystemBooted = false;
+ boolean mForceDisplayEnabled = false;
boolean mShowingBootMessages = false;
int mInitialDisplayWidth = 0;
int mInitialDisplayHeight = 0;
@@ -2189,7 +2190,7 @@
// to hold off on removing the window until the animation is done.
// If the display is frozen, just remove immediately, since the
// animation wouldn't be seen.
- if (win.mSurface != null && !mDisplayFrozen && mPolicy.isScreenOn()) {
+ if (win.mSurface != null && !mDisplayFrozen && mDisplayEnabled && mPolicy.isScreenOn()) {
// If we are not currently running the exit animation, we
// need to see about starting one.
if (wasVisible=win.isWinVisibleLw()) {
@@ -2536,6 +2537,12 @@
win.mRelayoutCalled = true;
final int oldVisibility = win.mViewVisibility;
win.mViewVisibility = viewVisibility;
+ if (DEBUG_SCREEN_ON) {
+ RuntimeException stack = new RuntimeException();
+ stack.fillInStackTrace();
+ Slog.i(TAG, "Relayout " + win + ": oldVis=" + oldVisibility
+ + " newVis=" + viewVisibility, stack);
+ }
if (viewVisibility == View.VISIBLE &&
(win.mAppToken == null || !win.mAppToken.clientHidden)) {
displayed = !win.isVisibleLw();
@@ -2556,7 +2563,7 @@
if (displayed) {
if (win.mSurface != null && !win.mDrawPending
&& !win.mCommitDrawPending && !mDisplayFrozen
- && mPolicy.isScreenOn()) {
+ && mDisplayEnabled && mPolicy.isScreenOn()) {
applyEnterAnimationLocked(win);
}
if ((win.mAttrs.flags
@@ -2849,7 +2856,7 @@
// frozen, there is no reason to animate and it can cause strange
// artifacts when we unfreeze the display if some different animation
// is running.
- if (!mDisplayFrozen && mPolicy.isScreenOn()) {
+ if (!mDisplayFrozen && mDisplayEnabled && mPolicy.isScreenOn()) {
int anim = mPolicy.selectAnimationLw(win, transit);
int attr = -1;
Animation a = null;
@@ -2935,7 +2942,7 @@
// frozen, there is no reason to animate and it can cause strange
// artifacts when we unfreeze the display if some different animation
// is running.
- if (!mDisplayFrozen && mPolicy.isScreenOn()) {
+ if (!mDisplayFrozen && mDisplayEnabled && mPolicy.isScreenOn()) {
Animation a;
if (mNextAppTransitionPackage != null) {
a = loadAnimation(mNextAppTransitionPackage, enter ?
@@ -3501,7 +3508,7 @@
if (DEBUG_APP_TRANSITIONS) Slog.v(
TAG, "Prepare app transition: transit=" + transit
+ " mNextAppTransition=" + mNextAppTransition);
- if (!mDisplayFrozen && mPolicy.isScreenOn()) {
+ if (!mDisplayFrozen && mDisplayEnabled && mPolicy.isScreenOn()) {
if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET
|| mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) {
mNextAppTransition = transit;
@@ -3585,7 +3592,7 @@
// If the display is frozen, we won't do anything until the
// actual window is displayed so there is no reason to put in
// the starting window.
- if (mDisplayFrozen || !mPolicy.isScreenOn()) {
+ if (mDisplayFrozen || !mDisplayEnabled || !mPolicy.isScreenOn()) {
return;
}
@@ -3867,7 +3874,7 @@
// If we are preparing an app transition, then delay changing
// the visibility of this token until we execute that transition.
- if (!mDisplayFrozen && mPolicy.isScreenOn()
+ if (!mDisplayFrozen && mDisplayEnabled && mPolicy.isScreenOn()
&& mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
// Already in requested state, don't do anything more.
if (wtoken.hiddenRequested != visible) {
@@ -4688,6 +4695,10 @@
}
mSystemBooted = true;
hideBootMessagesLocked();
+ // If the screen still doesn't come up after 30 seconds, give
+ // up and turn it on.
+ Message msg = mH.obtainMessage(H.BOOT_TIMEOUT);
+ mH.sendMessageDelayed(msg, 30*1000);
}
performEnableScreen();
@@ -4703,6 +4714,17 @@
mH.sendMessage(mH.obtainMessage(H.ENABLE_SCREEN));
}
+ public void performBootTimeout() {
+ synchronized(mWindowMap) {
+ if (mDisplayEnabled) {
+ return;
+ }
+ Slog.w(TAG, "***** BOOT TIMEOUT: forcing display enabled");
+ mForceDisplayEnabled = true;
+ }
+ performEnableScreen();
+ }
+
public void performEnableScreen() {
synchronized(mWindowMap) {
if (mDisplayEnabled) {
@@ -4712,19 +4734,55 @@
return;
}
- // Don't enable the screen until all existing windows
- // have been drawn.
- final int N = mWindows.size();
- for (int i=0; i<N; i++) {
- WindowState w = mWindows.get(i);
- if (w.isVisibleLw() && !w.mObscured && !w.isDrawnLw()) {
+ if (!mForceDisplayEnabled) {
+ // Don't enable the screen until all existing windows
+ // have been drawn.
+ boolean haveBootMsg = false;
+ boolean haveApp = false;
+ boolean haveWallpaper = false;
+ boolean haveKeyguard = false;
+ final int N = mWindows.size();
+ for (int i=0; i<N; i++) {
+ WindowState w = mWindows.get(i);
+ if (w.isVisibleLw() && !w.mObscured && !w.isDrawnLw()) {
+ return;
+ }
+ if (w.isDrawnLw()) {
+ if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_BOOT_PROGRESS) {
+ haveBootMsg = true;
+ } else if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION) {
+ haveApp = true;
+ } else if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_WALLPAPER) {
+ haveWallpaper = true;
+ } else if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_KEYGUARD) {
+ haveKeyguard = true;
+ }
+ }
+ }
+
+ if (DEBUG_SCREEN_ON) {
+ Slog.i(TAG, "******** booted=" + mSystemBooted + " msg=" + mShowingBootMessages
+ + " haveBoot=" + haveBootMsg + " haveApp=" + haveApp
+ + " haveWall=" + haveWallpaper + " haveKeyguard=" + haveKeyguard);
+ }
+
+ // If we are turning on the screen to show the boot message,
+ // don't do it until the boot message is actually displayed.
+ if (!mSystemBooted && !haveBootMsg) {
+ return;
+ }
+
+ // If we are turning on the screen after the boot is completed
+ // normally, don't do so until we have the application and
+ // wallpaper.
+ if (mSystemBooted && ((!haveApp && !haveKeyguard) || !haveWallpaper)) {
return;
}
}
mDisplayEnabled = true;
+ if (DEBUG_SCREEN_ON) Slog.i(TAG, "******************** ENABLING SCREEN!");
if (false) {
- Slog.i(TAG, "ENABLING SCREEN!");
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
this.dump(null, pw, null);
@@ -4939,6 +4997,14 @@
dh = tmp;
rot = (rot == Surface.ROTATION_90) ? Surface.ROTATION_270 : Surface.ROTATION_90;
}
+ if (DEBUG_SCREENSHOT) {
+ Slog.i(TAG, "Screenshot: " + dw + "x" + dh + " from 0 to " + maxLayer);
+ for (int i=0; i<mWindows.size(); i++) {
+ Slog.i(TAG, mWindows.get(i) + ": " + mWindows.get(i).mLayer
+ + " animLayer=" + mWindows.get(i).mAnimLayer
+ + " surfaceLayer=" + mWindows.get(i).mSurfaceLayer);
+ }
+ }
rawss = Surface.screenshot(dw, dh, 0, maxLayer);
}
@@ -6239,6 +6305,7 @@
public static final int DRAG_START_TIMEOUT = 20;
public static final int DRAG_END_TIMEOUT = 21;
public static final int REPORT_HARD_KEYBOARD_STATUS_CHANGE = 22;
+ public static final int BOOT_TIMEOUT = 23;
private Session mLastReportedHold;
@@ -6549,6 +6616,11 @@
break;
}
+ case BOOT_TIMEOUT: {
+ performBootTimeout();
+ break;
+ }
+
case APP_FREEZE_TIMEOUT: {
synchronized (mWindowMap) {
Slog.w(TAG, "App freeze timeout expired.");
@@ -8300,7 +8372,7 @@
if (mDimAnimator != null && mDimAnimator.mDimShown) {
animating |= mDimAnimator.updateSurface(dimming, currentTime,
- mDisplayFrozen || !mPolicy.isScreenOn());
+ mDisplayFrozen || !mDisplayEnabled || !mPolicy.isScreenOn());
}
if (!blurring && mBlurShown) {
@@ -8498,12 +8570,44 @@
mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
}
}
-
+
+ mWindowMap.notifyAll();
+
// Check to see if we are now in a state where the screen should
// be enabled, because the window obscured flags have changed.
enableScreenIfNeededLocked();
}
-
+
+ public void waitForAllDrawn() {
+ synchronized (mWindowMap) {
+ while (true) {
+ final int N = mWindows.size();
+ boolean okay = true;
+ for (int i=0; i<N && okay; i++) {
+ WindowState w = mWindows.get(i);
+ if (DEBUG_SCREEN_ON) {
+ Slog.i(TAG, "Window " + w + " vis=" + w.isVisibleLw()
+ + " obscured=" + w.mObscured + " drawn=" + w.isDrawnLw());
+ }
+ if (w.isVisibleLw() && !w.mObscured && !w.isDrawnLw()) {
+ if (DEBUG_SCREEN_ON) {
+ Slog.i(TAG, "Window not yet drawn: " + w);
+ }
+ okay = false;
+ break;
+ }
+ }
+ if (okay) {
+ return;
+ }
+ try {
+ mWindowMap.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+
/**
* Must be called with the main window manager lock held.
*/
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 19c7ddd..f8925b8 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -244,9 +244,6 @@
}
}
-static inline uint16_t pack565(int r, int g, int b) {
- return (r<<11)|(g<<5)|b;
-}
void Layer::onDraw(const Region& clip) const
{
if (CC_UNLIKELY(mActiveBuffer == 0)) {
@@ -260,7 +257,8 @@
// figure out if there is something below us
Region under;
- const SurfaceFlinger::LayerVector& drawingLayers(mFlinger->mDrawingState.layersSortedByZ);
+ const SurfaceFlinger::LayerVector& drawingLayers(
+ mFlinger->mDrawingState.layersSortedByZ);
const size_t count = drawingLayers.size();
for (size_t i=0 ; i<count ; ++i) {
const sp<LayerBase>& layer(drawingLayers[i]);
@@ -276,7 +274,7 @@
return;
}
- GLenum target = mSurfaceTexture->getCurrentTextureTarget();
+ const GLenum target = GL_TEXTURE_EXTERNAL_OES;
glBindTexture(target, mTextureName);
if (getFiltering() || needsFiltering() || isFixedSize() || isCropped()) {
// TODO: we could be more subtle with isFixedSize()
@@ -439,9 +437,8 @@
recomputeVisibleRegions = true;
}
- const GLenum target(mSurfaceTexture->getCurrentTextureTarget());
- glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// update the layer size and release freeze-lock
const Layer::State& front(drawingState());
@@ -541,9 +538,9 @@
snprintf(buffer, SIZE,
" "
"format=%2d, activeBuffer=[%4ux%4u:%4u,%3X],"
- " freezeLock=%p, queued-frames=%d\n",
+ " freezeLock=%p, transform-hint=0x%02x, queued-frames=%d\n",
mFormat, w0, h0, s0,f0,
- getFreezeLock().get(), mQueuedFrames);
+ getFreezeLock().get(), getTransformHint(), mQueuedFrames);
result.append(buffer);
@@ -559,9 +556,21 @@
// need a hardware-protected path to external video sink
usage |= GraphicBuffer::USAGE_PROTECTED;
}
+ usage |= GraphicBuffer::USAGE_HW_COMPOSER;
return usage;
}
+uint32_t Layer::getTransformHint() const {
+ uint32_t orientation = 0;
+ if (!mFlinger->mDebugDisableTransformHint) {
+ orientation = getOrientation();
+ if (orientation & Transform::ROT_INVALID) {
+ orientation = 0;
+ }
+ }
+ return orientation;
+}
+
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 5f0be80..d06a35f 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -89,6 +89,7 @@
void onFrameQueued();
virtual sp<ISurface> createSurface();
uint32_t getEffectiveUsage(uint32_t usage) const;
+ uint32_t getTransformHint() const;
bool isCropped() const;
static bool getOpacityForFormat(uint32_t format);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 50afb3d..0080202 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -93,6 +93,7 @@
mDebugBackground(0),
mDebugDDMS(0),
mDebugDisableHWC(0),
+ mDebugDisableTransformHint(0),
mDebugInSwapBuffers(0),
mLastSwapBufferTime(0),
mDebugInTransaction(0),
@@ -822,7 +823,7 @@
hwc_layer_t* const cur(hwc.getLayers());
for (size_t i=0 ; cur && i<count ; i++) {
currentLayers[i]->setGeometry(&cur[i]);
- if (mDebugDisableHWC) {
+ if (mDebugDisableHWC || mDebugRegion) {
cur[i].compositionType = HWC_FRAMEBUFFER;
cur[i].flags |= HWC_SKIP_LAYER;
}
@@ -974,6 +975,10 @@
{
const DisplayHardware& hw(graphicPlane(0).displayHardware());
const uint32_t flags = hw.getFlags();
+ const int32_t height = hw.getHeight();
+ if (mInvalidRegion.isEmpty()) {
+ return;
+ }
if (!((flags & DisplayHardware::SWAP_RECTANGLE) ||
(flags & DisplayHardware::BUFFER_PRESERVED))) {
@@ -999,26 +1004,21 @@
while (it != end) {
const Rect& r = *it++;
GLfloat vertices[][2] = {
- { r.left, r.top },
- { r.left, r.bottom },
- { r.right, r.bottom },
- { r.right, r.top }
+ { r.left, height - r.top },
+ { r.left, height - r.bottom },
+ { r.right, height - r.bottom },
+ { r.right, height - r.top }
};
glVertexPointer(2, GL_FLOAT, 0, vertices);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
- if (mInvalidRegion.isEmpty()) {
- mDirtyRegion.dump("mDirtyRegion");
- mInvalidRegion.dump("mInvalidRegion");
- }
hw.flip(mInvalidRegion);
if (mDebugRegion > 1)
usleep(mDebugRegion * 1000);
glEnable(GL_SCISSOR_TEST);
- //mDirtyRegion.dump("mDirtyRegion");
}
void SurfaceFlinger::drawWormhole() const
@@ -1581,7 +1581,7 @@
HWComposer& hwc(hw.getHwComposer());
snprintf(buffer, SIZE, " h/w composer %s and %s\n",
hwc.initCheck()==NO_ERROR ? "present" : "not present",
- mDebugDisableHWC ? "disabled" : "enabled");
+ (mDebugDisableHWC || mDebugRegion) ? "disabled" : "enabled");
result.append(buffer);
hwc.dump(result, buffer, SIZE);
@@ -1660,21 +1660,15 @@
case 1002: // SHOW_UPDATES
n = data.readInt32();
mDebugRegion = n ? n : (mDebugRegion ? 0 : 1);
+ invalidateHwcGeometry();
+ repaintEverything();
return NO_ERROR;
case 1003: // SHOW_BACKGROUND
n = data.readInt32();
mDebugBackground = n ? 1 : 0;
return NO_ERROR;
- case 1008: // toggle use of hw composer
- n = data.readInt32();
- mDebugDisableHWC = n ? 1 : 0;
- invalidateHwcGeometry();
- // fall-through...
case 1004:{ // repaint everything
- Mutex::Autolock _l(mStateLock);
- const DisplayHardware& hw(graphicPlane(0).displayHardware());
- mDirtyRegion.set(hw.bounds()); // careful that's not thread-safe
- signalEvent();
+ repaintEverything();
return NO_ERROR;
}
case 1005:{ // force transaction
@@ -1690,6 +1684,18 @@
mFreezeCount = data.readInt32();
mFreezeDisplayTime = 0;
return NO_ERROR;
+ case 1008: // toggle use of hw composer
+ n = data.readInt32();
+ mDebugDisableHWC = n ? 1 : 0;
+ invalidateHwcGeometry();
+ repaintEverything();
+ return NO_ERROR;
+ case 1009: // toggle use of transform hint
+ n = data.readInt32();
+ mDebugDisableTransformHint = n ? 1 : 0;
+ invalidateHwcGeometry();
+ repaintEverything();
+ return NO_ERROR;
case 1010: // interrogate.
reply->writeInt32(0);
reply->writeInt32(0);
@@ -1707,6 +1713,13 @@
return err;
}
+void SurfaceFlinger::repaintEverything() {
+ Mutex::Autolock _l(mStateLock);
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ mDirtyRegion.set(hw.bounds()); // careful that's not thread-safe
+ signalEvent();
+}
+
// ---------------------------------------------------------------------------
status_t SurfaceFlinger::renderScreenToTextureLocked(DisplayID dpy,
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 1738238..5f8eb08 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -278,6 +278,7 @@
void handleRepaint();
void postFramebuffer();
void composeSurfaces(const Region& dirty);
+ void repaintEverything();
ssize_t addClientLayer(const sp<Client>& client,
@@ -373,6 +374,7 @@
int mDebugBackground;
int mDebugDDMS;
int mDebugDisableHWC;
+ int mDebugDisableTransformHint;
volatile nsecs_t mDebugInSwapBuffers;
nsecs_t mLastSwapBufferTime;
volatile nsecs_t mDebugInTransaction;
diff --git a/services/surfaceflinger/SurfaceTextureLayer.cpp b/services/surfaceflinger/SurfaceTextureLayer.cpp
index 79cd0c3..4390ca1 100644
--- a/services/surfaceflinger/SurfaceTextureLayer.cpp
+++ b/services/surfaceflinger/SurfaceTextureLayer.cpp
@@ -57,16 +57,10 @@
status_t res = SurfaceTexture::queueBuffer(buf, timestamp,
outWidth, outHeight, outTransform);
-
sp<Layer> layer(mLayer.promote());
if (layer != NULL) {
- uint32_t orientation = layer->getOrientation();
- if (orientation & Transform::ROT_INVALID) {
- orientation = 0;
- }
- *outTransform = orientation;
+ *outTransform = layer->getTransformHint();
}
-
return res;
}
diff --git a/services/surfaceflinger/TextureManager.cpp b/services/surfaceflinger/TextureManager.cpp
deleted file mode 100644
index bb63c37..0000000
--- a/services/surfaceflinger/TextureManager.cpp
+++ /dev/null
@@ -1,341 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/Log.h>
-
-#include <ui/GraphicBuffer.h>
-
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-
-#include <hardware/hardware.h>
-
-#include "clz.h"
-#include "DisplayHardware/DisplayHardware.h"
-#include "GLExtensions.h"
-#include "TextureManager.h"
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-TextureManager::TextureManager()
- : mGLExtensions(GLExtensions::getInstance())
-{
-}
-
-GLenum TextureManager::getTextureTarget(const Image* image) {
-#if defined(GL_OES_EGL_image_external)
- switch (image->target) {
- case Texture::TEXTURE_EXTERNAL:
- return GL_TEXTURE_EXTERNAL_OES;
- }
-#endif
- return GL_TEXTURE_2D;
-}
-
-status_t TextureManager::initTexture(Texture* texture)
-{
- if (texture->name != -1UL)
- return INVALID_OPERATION;
-
- GLuint textureName = -1;
- glGenTextures(1, &textureName);
- texture->name = textureName;
- texture->width = 0;
- texture->height = 0;
-
- const GLenum target = GL_TEXTURE_2D;
- glBindTexture(target, textureName);
- glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-
- return NO_ERROR;
-}
-
-status_t TextureManager::initTexture(Image* pImage, int32_t format)
-{
- if (pImage->name != -1UL)
- return INVALID_OPERATION;
-
- GLuint textureName = -1;
- glGenTextures(1, &textureName);
- pImage->name = textureName;
- pImage->width = 0;
- pImage->height = 0;
-
- GLenum target = GL_TEXTURE_2D;
-#if defined(GL_OES_EGL_image_external)
- if (GLExtensions::getInstance().haveTextureExternal()) {
- if (format && isYuvFormat(format)) {
- target = GL_TEXTURE_EXTERNAL_OES;
- pImage->target = Texture::TEXTURE_EXTERNAL;
- }
- }
-#endif
-
- glBindTexture(target, textureName);
- glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-
- return NO_ERROR;
-}
-
-bool TextureManager::isSupportedYuvFormat(int format)
-{
- switch (format) {
- case HAL_PIXEL_FORMAT_YV12:
- return true;
- }
- return false;
-}
-
-bool TextureManager::isYuvFormat(int format)
-{
- switch (format) {
- // supported YUV formats
- case HAL_PIXEL_FORMAT_YV12:
- // Legacy/deprecated YUV formats
- case HAL_PIXEL_FORMAT_YCbCr_422_SP:
- case HAL_PIXEL_FORMAT_YCrCb_420_SP:
- case HAL_PIXEL_FORMAT_YCbCr_422_I:
- return true;
- }
-
- // Any OEM format needs to be considered
- if (format>=0x100 && format<=0x1FF)
- return true;
-
- return false;
-}
-
-status_t TextureManager::initEglImage(Image* pImage,
- EGLDisplay dpy, const sp<GraphicBuffer>& buffer)
-{
- status_t err = NO_ERROR;
- if (!pImage->dirty) return err;
-
- // free the previous image
- if (pImage->image != EGL_NO_IMAGE_KHR) {
- eglDestroyImageKHR(dpy, pImage->image);
- pImage->image = EGL_NO_IMAGE_KHR;
- }
-
- // construct an EGL_NATIVE_BUFFER_ANDROID
- ANativeWindowBuffer* clientBuf = buffer->getNativeBuffer();
-
- // create the new EGLImageKHR
- const EGLint attrs[] = {
- EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
- EGL_NONE, EGL_NONE
- };
- pImage->image = eglCreateImageKHR(
- dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
- (EGLClientBuffer)clientBuf, attrs);
-
- if (pImage->image != EGL_NO_IMAGE_KHR) {
- if (pImage->name == -1UL) {
- initTexture(pImage, buffer->format);
- }
- const GLenum target = getTextureTarget(pImage);
- glBindTexture(target, pImage->name);
- glEGLImageTargetTexture2DOES(target, (GLeglImageOES)pImage->image);
- GLint error = glGetError();
- if (error != GL_NO_ERROR) {
- LOGE("glEGLImageTargetTexture2DOES(%p) failed err=0x%04x",
- pImage->image, error);
- err = INVALID_OPERATION;
- } else {
- // Everything went okay!
- pImage->dirty = false;
- pImage->width = clientBuf->width;
- pImage->height = clientBuf->height;
- }
- } else {
- LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError());
- err = INVALID_OPERATION;
- }
- return err;
-}
-
-status_t TextureManager::loadTexture(Texture* texture,
- const Region& dirty, const GGLSurface& t)
-{
- if (texture->name == -1UL) {
- status_t err = initTexture(texture);
- LOGE_IF(err, "loadTexture failed in initTexture (%s)", strerror(err));
- if (err != NO_ERROR) return err;
- }
-
- if (texture->target != Texture::TEXTURE_2D)
- return INVALID_OPERATION;
-
- glBindTexture(GL_TEXTURE_2D, texture->name);
-
- /*
- * In OpenGL ES we can't specify a stride with glTexImage2D (however,
- * GL_UNPACK_ALIGNMENT is a limited form of stride).
- * So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we
- * need to do something reasonable (here creating a bigger texture).
- *
- * extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT);
- *
- * This situation doesn't happen often, but some h/w have a limitation
- * for their framebuffer (eg: must be multiple of 8 pixels), and
- * we need to take that into account when using these buffers as
- * textures.
- *
- * This should never be a problem with POT textures
- */
-
- int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format));
- unpack = 1 << ((unpack > 3) ? 3 : unpack);
- glPixelStorei(GL_UNPACK_ALIGNMENT, unpack);
-
- /*
- * round to POT if needed
- */
- if (!mGLExtensions.haveNpot()) {
- texture->NPOTAdjust = true;
- }
-
- if (texture->NPOTAdjust) {
- // find the smallest power-of-two that will accommodate our surface
- texture->potWidth = 1 << (31 - clz(t.width));
- texture->potHeight = 1 << (31 - clz(t.height));
- if (texture->potWidth < t.width) texture->potWidth <<= 1;
- if (texture->potHeight < t.height) texture->potHeight <<= 1;
- texture->wScale = float(t.width) / texture->potWidth;
- texture->hScale = float(t.height) / texture->potHeight;
- } else {
- texture->potWidth = t.width;
- texture->potHeight = t.height;
- }
-
- Rect bounds(dirty.bounds());
- GLvoid* data = 0;
- if (texture->width != t.width || texture->height != t.height) {
- texture->width = t.width;
- texture->height = t.height;
-
- // texture size changed, we need to create a new one
- bounds.set(Rect(t.width, t.height));
- if (t.width == texture->potWidth &&
- t.height == texture->potHeight) {
- // we can do it one pass
- data = t.data;
- }
-
- if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
- glTexImage2D(GL_TEXTURE_2D, 0,
- GL_RGB, texture->potWidth, texture->potHeight, 0,
- GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data);
- } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
- glTexImage2D(GL_TEXTURE_2D, 0,
- GL_RGBA, texture->potWidth, texture->potHeight, 0,
- GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data);
- } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
- t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
- glTexImage2D(GL_TEXTURE_2D, 0,
- GL_RGBA, texture->potWidth, texture->potHeight, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, data);
- } else if (isSupportedYuvFormat(t.format)) {
- // just show the Y plane of YUV buffers
- glTexImage2D(GL_TEXTURE_2D, 0,
- GL_LUMINANCE, texture->potWidth, texture->potHeight, 0,
- GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
- } else {
- // oops, we don't handle this format!
- LOGE("texture=%d, using format %d, which is not "
- "supported by the GL", texture->name, t.format);
- }
- }
- if (!data) {
- if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
- glTexSubImage2D(GL_TEXTURE_2D, 0,
- 0, bounds.top, t.width, bounds.height(),
- GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
- t.data + bounds.top*t.stride*2);
- } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
- glTexSubImage2D(GL_TEXTURE_2D, 0,
- 0, bounds.top, t.width, bounds.height(),
- GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4,
- t.data + bounds.top*t.stride*2);
- } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
- t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
- glTexSubImage2D(GL_TEXTURE_2D, 0,
- 0, bounds.top, t.width, bounds.height(),
- GL_RGBA, GL_UNSIGNED_BYTE,
- t.data + bounds.top*t.stride*4);
- } else if (isSupportedYuvFormat(t.format)) {
- // just show the Y plane of YUV buffers
- glTexSubImage2D(GL_TEXTURE_2D, 0,
- 0, bounds.top, t.width, bounds.height(),
- GL_LUMINANCE, GL_UNSIGNED_BYTE,
- t.data + bounds.top*t.stride);
- }
- }
- return NO_ERROR;
-}
-
-void TextureManager::activateTexture(const Texture& texture, bool filter)
-{
- const GLenum target = getTextureTarget(&texture);
- if (target == GL_TEXTURE_2D) {
- glBindTexture(GL_TEXTURE_2D, texture.name);
- glEnable(GL_TEXTURE_2D);
-#if defined(GL_OES_EGL_image_external)
- if (GLExtensions::getInstance().haveTextureExternal()) {
- glDisable(GL_TEXTURE_EXTERNAL_OES);
- }
- } else {
- glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture.name);
- glEnable(GL_TEXTURE_EXTERNAL_OES);
- glDisable(GL_TEXTURE_2D);
-#endif
- }
-
- if (filter) {
- glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- } else {
- glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- }
-}
-
-void TextureManager::deactivateTextures()
-{
- glDisable(GL_TEXTURE_2D);
-#if defined(GL_OES_EGL_image_external)
- if (GLExtensions::getInstance().haveTextureExternal()) {
- glDisable(GL_TEXTURE_EXTERNAL_OES);
- }
-#endif
-}
-
-// ---------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/services/surfaceflinger/TextureManager.h b/services/surfaceflinger/TextureManager.h
deleted file mode 100644
index 18c43486..0000000
--- a/services/surfaceflinger/TextureManager.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#ifndef ANDROID_TEXTURE_MANAGER_H
-#define ANDROID_TEXTURE_MANAGER_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES/gl.h>
-
-#include <ui/Region.h>
-
-#include <pixelflinger/pixelflinger.h>
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-class GLExtensions;
-class GraphicBuffer;
-
-// ---------------------------------------------------------------------------
-
-struct Image {
- enum { TEXTURE_2D=0, TEXTURE_EXTERNAL=1 };
- Image() : name(-1U), image(EGL_NO_IMAGE_KHR), width(0), height(0),
- dirty(1), target(TEXTURE_2D) { }
- GLuint name;
- EGLImageKHR image;
- GLuint width;
- GLuint height;
- unsigned dirty : 1;
- unsigned target : 1;
-};
-
-struct Texture : public Image {
- Texture() : Image(), NPOTAdjust(0) { }
- GLuint potWidth;
- GLuint potHeight;
- GLfloat wScale;
- GLfloat hScale;
- unsigned NPOTAdjust : 1;
-};
-
-// ---------------------------------------------------------------------------
-
-class TextureManager {
- const GLExtensions& mGLExtensions;
- static status_t initTexture(Image* texture, int32_t format);
- static status_t initTexture(Texture* texture);
- static bool isSupportedYuvFormat(int format);
- static bool isYuvFormat(int format);
- static GLenum getTextureTarget(const Image* pImage);
-public:
-
- TextureManager();
-
- // load bitmap data into the active buffer
- status_t loadTexture(Texture* texture,
- const Region& dirty, const GGLSurface& t);
-
- // make active buffer an EGLImage if needed
- status_t initEglImage(Image* texture,
- EGLDisplay dpy, const sp<GraphicBuffer>& buffer);
-
- // activate a texture
- static void activateTexture(const Texture& texture, bool filter);
-
- // deactivate a texture
- static void deactivateTextures();
-};
-
-// ---------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_TEXTURE_MANAGER_H
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 845aa3f..f67d251 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -79,6 +79,7 @@
import org.easymock.Capture;
import org.easymock.EasyMock;
import org.easymock.IAnswer;
+import org.easymock.IExpectationSetters;
import java.io.File;
import java.util.LinkedHashSet;
@@ -87,6 +88,8 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import libcore.io.IoUtils;
+
/**
* Tests for {@link NetworkPolicyManagerService}.
*/
@@ -117,6 +120,9 @@
private Binder mStubBinder = new Binder();
+ private long mStartTime;
+ private long mElapsedRealtime;
+
private static final int UID_A = android.os.Process.FIRST_APPLICATION_UID + 800;
private static final int UID_B = android.os.Process.FIRST_APPLICATION_UID + 801;
@@ -128,6 +134,8 @@
public void setUp() throws Exception {
super.setUp();
+ setCurrentTimeMillis(TEST_START);
+
// intercept various broadcasts, and pretend that uids have packages
mServiceContext = new BroadcastInterceptingContext(getContext()) {
@Override
@@ -160,8 +168,8 @@
};
mPolicyDir = getContext().getFilesDir();
- for (File file : mPolicyDir.listFiles()) {
- file.delete();
+ if (mPolicyDir.exists()) {
+ IoUtils.deleteContents(mPolicyDir);
}
mActivityManager = createMock(IActivityManager.class);
@@ -173,9 +181,8 @@
mConnManager = createMock(IConnectivityManager.class);
mNotifManager = createMock(INotificationManager.class);
- mService = new NetworkPolicyManagerService(
- mServiceContext, mActivityManager, mPowerManager, mStatsService,
- mNetworkManager, mTime, mPolicyDir);
+ mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager, mPowerManager,
+ mStatsService, mNetworkManager, mTime, mPolicyDir, true);
mService.bindConnectivityManager(mConnManager);
mService.bindNotificationManager(mNotifManager);
@@ -198,7 +205,7 @@
// expect to answer screen status during systemReady()
expect(mPowerManager.isScreenOn()).andReturn(true).atLeastOnce();
- expectTime(System.currentTimeMillis());
+ expectCurrentTime();
replay();
mService.systemReady();
@@ -485,7 +492,6 @@
}
public void testNetworkPolicyAppliedCycleLastMonth() throws Exception {
- long elapsedRealtime = 0;
NetworkState[] state = null;
NetworkStats stats = null;
Future<Void> future;
@@ -494,11 +500,13 @@
final long TIME_MAR_10 = 1173484800000L;
final int CYCLE_DAY = 15;
+ setCurrentTimeMillis(TIME_MAR_10);
+
// first, pretend that wifi network comes online. no policy active,
// which means we shouldn't push limit to interface.
state = new NetworkState[] { buildWifi() };
expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
- expectTime(TIME_MAR_10 + elapsedRealtime);
+ expectCurrentTime();
expectClearNotifications();
future = expectMeteredIfacesChanged();
@@ -510,10 +518,10 @@
// now change cycle to be on 15th, and test in early march, to verify we
// pick cycle day in previous month.
expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
- expectTime(TIME_MAR_10 + elapsedRealtime);
+ expectCurrentTime();
// pretend that 512 bytes total have happened
- stats = new NetworkStats(elapsedRealtime, 1)
+ stats = new NetworkStats(getElapsedRealtime(), 1)
.addIfaceValues(TEST_IFACE, 256L, 2L, 256L, 2L);
expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, TIME_MAR_10))
.andReturn(stats).atLeastOnce();
@@ -521,8 +529,6 @@
// TODO: consider making strongly ordered mock
expectRemoveInterfaceQuota(TEST_IFACE);
expectSetInterfaceQuota(TEST_IFACE, 1536L);
- expectRemoveInterfaceAlert(TEST_IFACE);
- expectSetInterfaceAlert(TEST_IFACE, 512L);
expectClearNotifications();
future = expectMeteredIfacesChanged(TEST_IFACE);
@@ -558,8 +564,6 @@
}
public void testOverWarningLimitNotification() throws Exception {
- long elapsedRealtime = 0;
- long currentTime = 0;
NetworkState[] state = null;
NetworkStats stats = null;
Future<Void> future;
@@ -569,14 +573,18 @@
final long TIME_MAR_10 = 1173484800000L;
final int CYCLE_DAY = 15;
+ setCurrentTimeMillis(TIME_MAR_10);
+
// assign wifi policy
- elapsedRealtime = 0;
- currentTime = TIME_MAR_10 + elapsedRealtime;
state = new NetworkState[] {};
+ stats = new NetworkStats(getElapsedRealtime(), 1)
+ .addIfaceValues(TEST_IFACE, 0L, 0L, 0L, 0L);
{
- expectTime(currentTime);
+ expectCurrentTime();
expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
+ expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
+ .andReturn(stats).atLeastOnce();
expectClearNotifications();
future = expectMeteredIfacesChanged();
@@ -589,22 +597,19 @@
}
// bring up wifi network
- elapsedRealtime += MINUTE_IN_MILLIS;
- currentTime = TIME_MAR_10 + elapsedRealtime;
- stats = new NetworkStats(elapsedRealtime, 1)
- .addIfaceValues(TEST_IFACE, 0L, 0L, 0L, 0L);
+ incrementCurrentTime(MINUTE_IN_MILLIS);
state = new NetworkState[] { buildWifi() };
+ stats = new NetworkStats(getElapsedRealtime(), 1)
+ .addIfaceValues(TEST_IFACE, 0L, 0L, 0L, 0L);
{
- expectTime(currentTime);
+ expectCurrentTime();
expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
- expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTime))
+ expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
.andReturn(stats).atLeastOnce();
expectRemoveInterfaceQuota(TEST_IFACE);
expectSetInterfaceQuota(TEST_IFACE, 2048L);
- expectRemoveInterfaceAlert(TEST_IFACE);
- expectSetInterfaceAlert(TEST_IFACE, 1024L);
expectClearNotifications();
future = expectMeteredIfacesChanged(TEST_IFACE);
@@ -616,14 +621,13 @@
}
// go over warning, which should kick notification
- elapsedRealtime += MINUTE_IN_MILLIS;
- currentTime = TIME_MAR_10 + elapsedRealtime;
- stats = new NetworkStats(elapsedRealtime, 1)
+ incrementCurrentTime(MINUTE_IN_MILLIS);
+ stats = new NetworkStats(getElapsedRealtime(), 1)
.addIfaceValues(TEST_IFACE, 1536L, 15L, 0L, 0L);
{
- expectTime(currentTime);
- expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTime))
+ expectCurrentTime();
+ expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
.andReturn(stats).atLeastOnce();
expectForceUpdate();
@@ -637,15 +641,15 @@
}
// go over limit, which should kick notification and dialog
- elapsedRealtime += MINUTE_IN_MILLIS;
- currentTime = TIME_MAR_10 + elapsedRealtime;
- stats = new NetworkStats(elapsedRealtime, 1)
+ incrementCurrentTime(MINUTE_IN_MILLIS);
+ stats = new NetworkStats(getElapsedRealtime(), 1)
.addIfaceValues(TEST_IFACE, 5120L, 512L, 0L, 0L);
{
- expectTime(currentTime);
- expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTime))
+ expectCurrentTime();
+ expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
.andReturn(stats).atLeastOnce();
+ expectPolicyDataEnable(TYPE_WIFI, false).atLeastOnce();
expectForceUpdate();
expectClearNotifications();
@@ -658,21 +662,23 @@
}
// now snooze policy, which should remove quota
- elapsedRealtime += MINUTE_IN_MILLIS;
- currentTime = TIME_MAR_10 + elapsedRealtime;
+ incrementCurrentTime(MINUTE_IN_MILLIS);
{
- expectTime(currentTime);
+ expectCurrentTime();
expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
- expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTime))
+ expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
.andReturn(stats).atLeastOnce();
+ expectPolicyDataEnable(TYPE_WIFI, true).atLeastOnce();
+ // snoozed interface still has high quota so background data is
+ // still restricted.
expectRemoveInterfaceQuota(TEST_IFACE);
- expectRemoveInterfaceAlert(TEST_IFACE);
+ expectSetInterfaceQuota(TEST_IFACE, Long.MAX_VALUE);
expectClearNotifications();
tag = expectEnqueueNotification();
- future = expectMeteredIfacesChanged();
+ future = expectMeteredIfacesChanged(TEST_IFACE);
replay();
mService.snoozePolicy(sTemplateWifi);
@@ -700,10 +706,10 @@
return new NetworkState(info, prop, null);
}
- private void expectTime(long currentTime) throws Exception {
+ private void expectCurrentTime() throws Exception {
expect(mTime.forceRefresh()).andReturn(false).anyTimes();
expect(mTime.hasCache()).andReturn(true).anyTimes();
- expect(mTime.currentTimeMillis()).andReturn(currentTime).anyTimes();
+ expect(mTime.currentTimeMillis()).andReturn(currentTimeMillis()).anyTimes();
expect(mTime.getCacheAge()).andReturn(0L).anyTimes();
expect(mTime.getCacheCertainty()).andReturn(0L).anyTimes();
}
@@ -770,6 +776,12 @@
return future;
}
+ private <T> IExpectationSetters<T> expectPolicyDataEnable(int type, boolean enabled)
+ throws Exception {
+ mConnManager.setPolicyDataEnable(type, enabled);
+ return expectLastCall();
+ }
+
private static class FutureAnswer extends AbstractFuture<Void> implements IAnswer<Void> {
@Override
public Void get() throws InterruptedException, ExecutionException {
@@ -818,6 +830,23 @@
Integer.toString(expected), actualTag.substring(actualTag.lastIndexOf(':') + 1));
}
+ private long getElapsedRealtime() {
+ return mElapsedRealtime;
+ }
+
+ private void setCurrentTimeMillis(long currentTimeMillis) {
+ mStartTime = currentTimeMillis;
+ mElapsedRealtime = 0L;
+ }
+
+ private long currentTimeMillis() {
+ return mStartTime + mElapsedRealtime;
+ }
+
+ private void incrementCurrentTime(long duration) {
+ mElapsedRealtime += duration;
+ }
+
private void replay() {
EasyMock.replay(mActivityManager, mPowerManager, mStatsService, mPolicyListener,
mNetworkManager, mTime, mConnManager, mNotifManager);
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index 3bd78e0..42ea4f29 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -36,8 +36,8 @@
import android.os.SystemProperties;
import android.preference.PreferenceManager;
import android.provider.Settings;
-import android.telephony.ServiceState;
import android.provider.Settings.SettingNotFoundException;
+import android.telephony.ServiceState;
import android.text.TextUtils;
import android.util.Log;
@@ -126,9 +126,10 @@
protected static final int EVENT_RESTART_RADIO = BASE + 26;
protected static final int EVENT_SET_INTERNAL_DATA_ENABLE = BASE + 27;
protected static final int EVENT_RESET_DONE = BASE + 28;
- public static final int CMD_SET_DATA_ENABLE = BASE + 29;
+ public static final int CMD_SET_USER_DATA_ENABLE = BASE + 29;
public static final int EVENT_CLEAN_UP_ALL_CONNECTIONS = BASE + 30;
public static final int CMD_SET_DEPENDENCY_MET = BASE + 31;
+ public static final int CMD_SET_POLICY_DATA_ENABLE = BASE + 32;
/***** Constants *****/
@@ -153,6 +154,8 @@
protected static final int APN_DELAY_MILLIS =
SystemProperties.getInt("persist.radio.apn_delay", 5000);
+ protected Object mDataEnabledLock = new Object();
+
// responds to the setInternalDataEnabled call - used internally to turn off data
// for example during emergency calls
protected boolean mInternalDataEnabled = true;
@@ -160,11 +163,12 @@
// responds to public (user) API to enable/disable data use
// independent of mInternalDataEnabled and requests for APN access
// persisted
- protected boolean mDataEnabled = true;
+ protected boolean mUserDataEnabled = true;
+ protected boolean mPolicyDataEnabled = true;
- protected boolean[] dataEnabled = new boolean[APN_NUM_TYPES];
+ private boolean[] dataEnabled = new boolean[APN_NUM_TYPES];
- protected int enabledCount = 0;
+ private int enabledCount = 0;
/* Currently requested APN type (TODO: This should probably be a parameter not a member) */
protected String mRequestedApnType = Phone.APN_TYPE_DEFAULT;
@@ -408,8 +412,8 @@
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
filter.addAction(INTENT_SET_FAIL_DATA_SETUP_COUNTER);
- mDataEnabled = Settings.Secure.getInt(mPhone.getContext().getContentResolver(),
- Settings.Secure.MOBILE_DATA, 1) == 1;
+ mUserDataEnabled = Settings.Secure.getInt(
+ mPhone.getContext().getContentResolver(), Settings.Secure.MOBILE_DATA, 1) == 1;
// TODO: Why is this registering the phone as the receiver of the intent
// and not its own handler?
@@ -630,13 +634,12 @@
onResetDone((AsyncResult) msg.obj);
break;
}
- case CMD_SET_DATA_ENABLE: {
- boolean enabled = (msg.arg1 == ENABLED) ? true : false;
- if (DBG) log("CMD_SET_DATA_ENABLE enabled=" + enabled);
- onSetDataEnabled(enabled);
+ case CMD_SET_USER_DATA_ENABLE: {
+ final boolean enabled = (msg.arg1 == ENABLED) ? true : false;
+ if (DBG) log("CMD_SET_USER_DATA_ENABLE enabled=" + enabled);
+ onSetUserDataEnabled(enabled);
break;
}
-
case CMD_SET_DEPENDENCY_MET: {
boolean met = (msg.arg1 == ENABLED) ? true : false;
if (DBG) log("CMD_SET_DEPENDENCY_MET met=" + met);
@@ -649,7 +652,11 @@
}
break;
}
-
+ case CMD_SET_POLICY_DATA_ENABLE: {
+ final boolean enabled = (msg.arg1 == ENABLED) ? true : false;
+ onSetPolicyDataEnabled(enabled);
+ break;
+ }
default:
Log.e("DATA", "Unidentified event msg=" + msg);
break;
@@ -662,8 +669,12 @@
* @return {@code false} if data connectivity has been explicitly disabled,
* {@code true} otherwise.
*/
- public synchronized boolean getAnyDataEnabled() {
- boolean result = (mInternalDataEnabled && mDataEnabled && (enabledCount != 0));
+ public boolean getAnyDataEnabled() {
+ final boolean result;
+ synchronized (mDataEnabledLock) {
+ result = (mInternalDataEnabled && mUserDataEnabled && mPolicyDataEnabled
+ && (enabledCount != 0));
+ }
if (!result && DBG) log("getAnyDataEnabled " + result);
return result;
}
@@ -985,18 +996,18 @@
return true;
}
- protected void onSetInternalDataEnabled(boolean enable) {
- boolean prevEnabled = getAnyDataEnabled();
- if (mInternalDataEnabled != enable) {
- synchronized (this) {
- mInternalDataEnabled = enable;
- }
- if (prevEnabled != getAnyDataEnabled()) {
- if (!prevEnabled) {
- resetAllRetryCounts();
- onTrySetupData(Phone.REASON_DATA_ENABLED);
- } else {
- cleanUpAllConnections(null);
+ protected void onSetInternalDataEnabled(boolean enabled) {
+ synchronized (mDataEnabledLock) {
+ final boolean prevEnabled = getAnyDataEnabled();
+ if (mInternalDataEnabled != enabled) {
+ mInternalDataEnabled = enabled;
+ if (prevEnabled != getAnyDataEnabled()) {
+ if (!prevEnabled) {
+ resetAllRetryCounts();
+ onTrySetupData(Phone.REASON_DATA_ENABLED);
+ } else {
+ cleanUpAllConnections(null);
+ }
}
}
}
@@ -1010,20 +1021,20 @@
public abstract boolean isAnyActiveDataConnections();
- protected void onSetDataEnabled(boolean enable) {
- boolean prevEnabled = getAnyDataEnabled();
- if (mDataEnabled != enable) {
- synchronized (this) {
- mDataEnabled = enable;
- }
- Settings.Secure.putInt(mPhone.getContext().getContentResolver(),
- Settings.Secure.MOBILE_DATA, enable ? 1 : 0);
- if (prevEnabled != getAnyDataEnabled()) {
- if (!prevEnabled) {
- resetAllRetryCounts();
- onTrySetupData(Phone.REASON_DATA_ENABLED);
- } else {
- onCleanUpAllConnections(Phone.REASON_DATA_DISABLED);
+ protected void onSetUserDataEnabled(boolean enabled) {
+ synchronized (mDataEnabledLock) {
+ final boolean prevEnabled = getAnyDataEnabled();
+ if (mUserDataEnabled != enabled) {
+ mUserDataEnabled = enabled;
+ Settings.Secure.putInt(mPhone.getContext().getContentResolver(),
+ Settings.Secure.MOBILE_DATA, enabled ? 1 : 0);
+ if (prevEnabled != getAnyDataEnabled()) {
+ if (!prevEnabled) {
+ resetAllRetryCounts();
+ onTrySetupData(Phone.REASON_DATA_ENABLED);
+ } else {
+ onCleanUpAllConnections(Phone.REASON_DATA_DISABLED);
+ }
}
}
}
@@ -1032,6 +1043,23 @@
protected void onSetDependencyMet(String apnType, boolean met) {
}
+ protected void onSetPolicyDataEnabled(boolean enabled) {
+ synchronized (mDataEnabledLock) {
+ final boolean prevEnabled = getAnyDataEnabled();
+ if (mPolicyDataEnabled != enabled) {
+ mPolicyDataEnabled = enabled;
+ if (prevEnabled != getAnyDataEnabled()) {
+ if (!prevEnabled) {
+ resetAllRetryCounts();
+ onTrySetupData(Phone.REASON_DATA_ENABLED);
+ } else {
+ onCleanUpAllConnections(Phone.REASON_DATA_DISABLED);
+ }
+ }
+ }
+ }
+ }
+
protected String getReryConfig(boolean forDefault) {
int rt = mPhone.getServiceState().getRadioTechnology();
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index 800615c..1a077d0 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -175,6 +175,11 @@
@Override
protected boolean isDataAllowed() {
+ final boolean internalDataEnabled;
+ synchronized (mDataEnabledLock) {
+ internalDataEnabled = mInternalDataEnabled;
+ }
+
int psState = mCdmaPhone.mSST.getCurrentDataConnectionState();
boolean roaming = (mPhone.getServiceState().getRoaming() && !getDataOnRoamingEnabled());
boolean desiredPowerState = mCdmaPhone.mSST.getDesiredPowerState();
@@ -187,7 +192,7 @@
(mCdmaPhone.mSST.isConcurrentVoiceAndDataAllowed() ||
mPhone.getState() == Phone.State.IDLE) &&
!roaming &&
- mInternalDataEnabled &&
+ internalDataEnabled &&
desiredPowerState &&
!mPendingRestartRadio &&
!mCdmaPhone.needsOtaServiceProvisioning();
@@ -205,7 +210,7 @@
reason += " - concurrentVoiceAndData not allowed and state= " + mPhone.getState();
}
if (roaming) reason += " - Roaming";
- if (!mInternalDataEnabled) reason += " - mInternalDataEnabled= false";
+ if (!internalDataEnabled) reason += " - mInternalDataEnabled= false";
if (!desiredPowerState) reason += " - desiredPowerState= false";
if (mPendingRestartRadio) reason += " - mPendingRestartRadio= true";
if (mCdmaPhone.needsOtaServiceProvisioning()) reason += " - needs Provisioning";
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 3236901..c8671c1 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -27,14 +27,14 @@
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.LinkAddress;
+import android.net.LinkCapabilities;
+import android.net.LinkProperties;
import android.net.LinkProperties.CompareResult;
+import android.net.NetworkConfig;
import android.net.NetworkUtils;
import android.net.ProxyProperties;
import android.net.TrafficStats;
import android.net.Uri;
-import android.net.LinkCapabilities;
-import android.net.LinkProperties;
-import android.net.NetworkConfig;
import android.os.AsyncResult;
import android.os.Message;
import android.os.SystemClock;
@@ -49,34 +49,27 @@
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
-import android.preference.PreferenceManager;
-import com.android.internal.R;
import com.android.internal.telephony.ApnContext;
import com.android.internal.telephony.ApnSetting;
import com.android.internal.telephony.DataCallState;
import com.android.internal.telephony.DataConnection;
+import com.android.internal.telephony.DataConnection.FailCause;
import com.android.internal.telephony.DataConnection.UpdateLinkPropertyResult;
import com.android.internal.telephony.DataConnectionAc;
import com.android.internal.telephony.DataConnectionTracker;
+import com.android.internal.telephony.EventLogTags;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneBase;
-import com.android.internal.telephony.RetryManager;
-import com.android.internal.telephony.EventLogTags;
-import com.android.internal.telephony.DataConnection.FailCause;
import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.RetryManager;
import com.android.internal.util.AsyncChannel;
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.List;
-import java.util.Map;
import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
/**
* {@hide}
@@ -523,16 +516,18 @@
* {@code true} otherwise.
*/
@Override
- public synchronized boolean getAnyDataEnabled() {
- if (!(mInternalDataEnabled && mDataEnabled)) return false;
- for (ApnContext apnContext : mApnContexts.values()) {
- // Make sure we dont have a context that going down
- // and is explicitly disabled.
- if (isDataAllowed(apnContext)) {
- return true;
+ public boolean getAnyDataEnabled() {
+ synchronized (mDataEnabledLock) {
+ if (!(mInternalDataEnabled && mUserDataEnabled && mPolicyDataEnabled)) return false;
+ for (ApnContext apnContext : mApnContexts.values()) {
+ // Make sure we dont have a context that going down
+ // and is explicitly disabled.
+ if (isDataAllowed(apnContext)) {
+ return true;
+ }
}
+ return false;
}
- return false;
}
private boolean isDataAllowed(ApnContext apnContext) {
@@ -570,6 +565,11 @@
@Override
protected boolean isDataAllowed() {
+ final boolean internalDataEnabled;
+ synchronized (mDataEnabledLock) {
+ internalDataEnabled = mInternalDataEnabled;
+ }
+
int gprsState = mPhone.getServiceStateTracker().getCurrentDataConnectionState();
boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
@@ -577,7 +577,7 @@
(gprsState == ServiceState.STATE_IN_SERVICE || mAutoAttachOnCreation) &&
mPhone.mIccRecords.getRecordsLoaded() &&
mPhone.getState() == Phone.State.IDLE &&
- mInternalDataEnabled &&
+ internalDataEnabled &&
(!mPhone.getServiceState().getRoaming() || getDataOnRoamingEnabled()) &&
!mIsPsRestricted &&
desiredPowerState;
@@ -590,7 +590,7 @@
if (mPhone.getState() != Phone.State.IDLE) {
reason += " - PhoneState= " + mPhone.getState();
}
- if (!mInternalDataEnabled) reason += " - mInternalDataEnabled= false";
+ if (!internalDataEnabled) reason += " - mInternalDataEnabled= false";
if (mPhone.getServiceState().getRoaming() && !getDataOnRoamingEnabled()) {
reason += " - Roaming and data roaming not enabled";
}
diff --git a/tests/RenderScriptTests/FBOTest/res/drawable/robot.png b/tests/RenderScriptTests/FBOTest/res/drawable-nodpi/robot.png
similarity index 100%
rename from tests/RenderScriptTests/FBOTest/res/drawable/robot.png
rename to tests/RenderScriptTests/FBOTest/res/drawable-nodpi/robot.png
Binary files differ
diff --git a/tests/RenderScriptTests/ImageProcessing/res/drawable/city.png b/tests/RenderScriptTests/ImageProcessing/res/drawable-nodpi/city.png
similarity index 100%
rename from tests/RenderScriptTests/ImageProcessing/res/drawable/city.png
rename to tests/RenderScriptTests/ImageProcessing/res/drawable-nodpi/city.png
Binary files differ
diff --git a/tests/RenderScriptTests/ModelViewer/res/drawable/robot.png b/tests/RenderScriptTests/ModelViewer/res/drawable-nodpi/robot.png
similarity index 100%
rename from tests/RenderScriptTests/ModelViewer/res/drawable/robot.png
rename to tests/RenderScriptTests/ModelViewer/res/drawable-nodpi/robot.png
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/checker.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/checker.png
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/checker.png
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/checker.png
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/data.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/data.png
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/data.png
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/data.png
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/flares.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/flares.png
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/flares.png
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/flares.png
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/globe.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/globe.png
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/globe.png
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/globe.png
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/leaf.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/leaf.png
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/leaf.png
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/leaf.png
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/light1.jpg b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/light1.jpg
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/light1.jpg
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/light1.jpg
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/space.jpg b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/space.jpg
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/space.jpg
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/space.jpg
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/test_pattern.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/test_pattern.png
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/test_pattern.png
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/test_pattern.png
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/torusmap.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/torusmap.png
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/torusmap.png
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/torusmap.png
Binary files differ
diff --git a/tests/RenderScriptTests/tests/res/drawable/test_pattern.png b/tests/RenderScriptTests/tests/res/drawable-nodpi/test_pattern.png
similarity index 100%
rename from tests/RenderScriptTests/tests/res/drawable/test_pattern.png
rename to tests/RenderScriptTests/tests/res/drawable-nodpi/test_pattern.png
Binary files differ
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
index 7d83a9f..ed29a78 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
@@ -451,6 +451,10 @@
return 0;
}
+ public void waitForAllDrawn() {
+ // TODO Auto-generated method stub
+ }
+
public IBinder asBinder() {
// TODO Auto-generated method stub
return null;
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index c20c716..956c3f2 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -16,23 +16,20 @@
package android.net.wifi;
-
-
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.net.ConnectivityManager;
import android.net.LinkCapabilities;
-import android.net.NetworkInfo;
import android.net.LinkProperties;
+import android.net.NetworkInfo;
import android.net.NetworkStateTracker;
import android.net.wifi.p2p.WifiP2pManager;
import android.os.Handler;
import android.os.Message;
+import android.util.Slog;
import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* Track the state of wifi for connectivity service.
@@ -44,6 +41,8 @@
private static final String NETWORKTYPE = "WIFI";
private static final String TAG = "WifiStateTracker";
+ private static final boolean LOGV = true;
+
private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
@@ -135,11 +134,14 @@
return mNetworkInfo.isAvailable();
}
- /**
- * @param enabled
- */
- public void setDataEnable(boolean enabled) {
- android.util.Log.d(TAG, "setDataEnabled: IGNORING enabled=" + enabled);
+ @Override
+ public void setUserDataEnable(boolean enabled) {
+ Slog.w(TAG, "ignoring setUserDataEnable(" + enabled + ")");
+ }
+
+ @Override
+ public void setPolicyDataEnable(boolean enabled) {
+ Slog.w(TAG, "ignoring setPolicyDataEnable(" + enabled + ")");
}
/**
diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
index 4dd856f..274edae 100644
--- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
@@ -46,9 +46,9 @@
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.URL;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Set;
/**
* {@link WifiWatchdogStateMachine} monitors the initial connection to a Wi-Fi
@@ -79,7 +79,7 @@
private static final int LOW_SIGNAL_CUTOFF = 1;
private static final long DEFAULT_DNS_CHECK_SHORT_INTERVAL_MS = 2 * 60 * 1000;
- private static final long DEFAULT_DNS_CHECK_LONG_INTERVAL_MS = 10 * 60 * 1000;
+ private static final long DEFAULT_DNS_CHECK_LONG_INTERVAL_MS = 30 * 60 * 1000;
private static final long DEFAULT_WALLED_GARDEN_INTERVAL_MS = 30 * 60 * 1000;
private static final int DEFAULT_MAX_SSID_BLACKLISTS = 7;
@@ -661,26 +661,34 @@
}
class DnsCheckingState extends State {
- int dnsCheckSuccesses = 0;
- int dnsCheckTries = 0;
- String dnsCheckLogStr = "";
- Set<Integer> ids = new HashSet<Integer>();
+ List<InetAddress> mDnsList;
+ int[] dnsCheckSuccesses;
+ String dnsCheckLogStr;
+ String[] dnsResponseStrs;
+ /** Keeps track of active dns pings. Map is from pingID to index in mDnsList */
+ HashMap<Integer, Integer> idDnsMap = new HashMap<Integer, Integer>();
@Override
public void enter() {
- dnsCheckSuccesses = 0;
- dnsCheckTries = 0;
- ids.clear();
- InetAddress dns = mDnsPinger.getDns();
+ mDnsList = mDnsPinger.getDnsList();
+ int numDnses = mDnsList.size();
+ dnsCheckSuccesses = new int[numDnses];
+ dnsResponseStrs = new String[numDnses];
+ for (int i = 0; i < numDnses; i++)
+ dnsResponseStrs[i] = "";
+
if (DBG) {
- Slog.d(WWSM_TAG, "Starting DNS pings at " + SystemClock.elapsedRealtime());
dnsCheckLogStr = String.format("Pinging %s on ssid [%s]: ",
- mDnsPinger.getDns(), mConnectionInfo.getSSID());
+ mDnsList, mConnectionInfo.getSSID());
+ Slog.d(WWSM_TAG, dnsCheckLogStr);
}
+ idDnsMap.clear();
for (int i=0; i < mNumDnsPings; i++) {
- ids.add(mDnsPinger.pingDnsAsync(dns, mDnsPingTimeoutMs,
- DNS_INTRATEST_PING_INTERVAL * i));
+ for (int j = 0; j < numDnses; j++) {
+ idDnsMap.put(mDnsPinger.pingDnsAsync(mDnsList.get(j), mDnsPingTimeoutMs,
+ DNS_INTRATEST_PING_INTERVAL * i), j);
+ }
}
}
@@ -693,27 +701,24 @@
int pingID = msg.arg1;
int pingResponseTime = msg.arg2;
- if (!ids.contains(pingID)) {
+ Integer dnsServerId = idDnsMap.get(pingID);
+ if (dnsServerId == null) {
Slog.w(WWSM_TAG, "Received a Dns response with unknown ID!");
return HANDLED;
}
- ids.remove(pingID);
- dnsCheckTries++;
+
+ idDnsMap.remove(pingID);
if (pingResponseTime >= 0)
- dnsCheckSuccesses++;
+ dnsCheckSuccesses[dnsServerId]++;
if (DBG) {
if (pingResponseTime >= 0) {
- dnsCheckLogStr += "|" + pingResponseTime;
+ dnsResponseStrs[dnsServerId] += "|" + pingResponseTime;
} else {
- dnsCheckLogStr += "|x";
+ dnsResponseStrs[dnsServerId] += "|x";
}
}
- if (VDBG) {
- Slog.v(WWSM_TAG, dnsCheckLogStr);
- }
-
/**
* After a full ping count, if we have more responses than this
* cutoff, the outcome is success; else it is 'failure'.
@@ -723,10 +728,10 @@
* Our final success count will be at least this big, so we're
* guaranteed to succeed.
*/
- if (dnsCheckSuccesses >= mMinDnsResponses) {
+ if (dnsCheckSuccesses[dnsServerId] >= mMinDnsResponses) {
// DNS CHECKS OK, NOW WALLED GARDEN
if (DBG) {
- Slog.d(WWSM_TAG, dnsCheckLogStr + "| SUCCESS");
+ Slog.d(WWSM_TAG, makeLogString() + " SUCCESS");
}
if (!shouldCheckWalledGarden()) {
@@ -748,14 +753,9 @@
return HANDLED;
}
- /**
- * Our final count will be at most the current count plus the
- * remaining pings - we're guaranteed to fail.
- */
- int remainingChecks = mNumDnsPings - dnsCheckTries;
- if (remainingChecks + dnsCheckSuccesses < mMinDnsResponses) {
+ if (idDnsMap.isEmpty()) {
if (DBG) {
- Slog.d(WWSM_TAG, dnsCheckLogStr + "| FAILURE");
+ Slog.d(WWSM_TAG, makeLogString() + " FAILURE");
}
transitionTo(mDnsCheckFailureState);
return HANDLED;
@@ -764,12 +764,18 @@
return HANDLED;
}
+ private String makeLogString() {
+ String logStr = dnsCheckLogStr;
+ for (String respStr : dnsResponseStrs)
+ logStr += " [" + respStr + "]";
+ return logStr;
+ }
+
@Override
public void exit() {
mDnsPinger.cancelPings();
}
-
private boolean shouldCheckWalledGarden() {
if (!mWalledGardenTestEnabled) {
if (VDBG)
@@ -809,7 +815,8 @@
int checkGuard = 0;
Long lastCheckTime = null;
- int curPingID = 0;
+ /** Keeps track of dns pings. Map is from pingID to InetAddress used for ping */
+ HashMap<Integer, InetAddress> pingInfoMap = new HashMap<Integer, InetAddress>();
@Override
public void enter() {
@@ -817,7 +824,7 @@
signalUnstable = false;
checkGuard++;
unstableSignalChecks = false;
- curPingID = 0;
+ pingInfoMap.clear();
triggerSingleDnsCheck();
}
@@ -853,32 +860,37 @@
return HANDLED;
}
lastCheckTime = SystemClock.elapsedRealtime();
- curPingID = mDnsPinger.pingDnsAsync(mDnsPinger.getDns(),
- mDnsPingTimeoutMs, 0);
+ pingInfoMap.clear();
+ for (InetAddress curDns: mDnsPinger.getDnsList()) {
+ pingInfoMap.put(mDnsPinger.pingDnsAsync(curDns, mDnsPingTimeoutMs, 0),
+ curDns);
+ }
return HANDLED;
case DnsPinger.DNS_PING_RESULT:
- if ((short) msg.arg1 != curPingID) {
- if (VDBG) {
- Slog.v(WWSM_TAG, "Received non-matching DnsPing w/ id: " +
- msg.arg1);
- }
+ InetAddress curDnsServer = pingInfoMap.get(msg.arg1);
+ if (curDnsServer == null) {
return HANDLED;
}
+ pingInfoMap.remove(msg.arg1);
int responseTime = msg.arg2;
if (responseTime >= 0) {
if (VDBG) {
- Slog.v(WWSM_TAG, "Ran a single DNS ping. Response time: "
- + responseTime);
+ Slog.v(WWSM_TAG, "Single DNS ping OK. Response time: "
+ + responseTime + " from DNS " + curDnsServer);
}
+ pingInfoMap.clear();
checkGuard++;
unstableSignalChecks = false;
triggerSingleDnsCheck();
} else {
- if (DBG) {
- Slog.d(WWSM_TAG, "Single dns ping failure. Starting full checks.");
+ if (pingInfoMap.isEmpty()) {
+ if (DBG) {
+ Slog.d(WWSM_TAG, "Single dns ping failure. All dns servers failed, "
+ + "starting full checks.");
+ }
+ transitionTo(mDnsCheckingState);
}
- transitionTo(mDnsCheckingState);
}
return HANDLED;
}