Merge "MtpData bmp format file can't recognize when copy bmp into DUT"
diff --git a/api/current.txt b/api/current.txt
index 880170d..b5fd82b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -21526,9 +21526,9 @@
method public static java.lang.String formatElapsedTime(long);
method public static java.lang.String formatElapsedTime(java.lang.StringBuilder, long);
method public static final java.lang.CharSequence formatSameDayTime(long, long, int, int);
- method public static java.lang.String getAMPMString(int);
- method public static java.lang.String getDayOfWeekString(int, int);
- method public static java.lang.String getMonthString(int, int);
+ method public static deprecated java.lang.String getAMPMString(int);
+ method public static deprecated java.lang.String getDayOfWeekString(int, int);
+ method public static deprecated java.lang.String getMonthString(int, int);
method public static java.lang.CharSequence getRelativeDateTimeString(android.content.Context, long, long, long, int);
method public static java.lang.CharSequence getRelativeTimeSpanString(long);
method public static java.lang.CharSequence getRelativeTimeSpanString(long, long, long);
@@ -21536,24 +21536,24 @@
method public static java.lang.CharSequence getRelativeTimeSpanString(android.content.Context, long, boolean);
method public static java.lang.CharSequence getRelativeTimeSpanString(android.content.Context, long);
method public static boolean isToday(long);
- field public static final java.lang.String ABBREV_MONTH_FORMAT = "%b";
+ field public static final deprecated java.lang.String ABBREV_MONTH_FORMAT = "%b";
field public static final java.lang.String ABBREV_WEEKDAY_FORMAT = "%a";
field public static final long DAY_IN_MILLIS = 86400000L; // 0x5265c00L
- field public static final int FORMAT_12HOUR = 64; // 0x40
- field public static final int FORMAT_24HOUR = 128; // 0x80
+ field public static final deprecated int FORMAT_12HOUR = 64; // 0x40
+ field public static final deprecated int FORMAT_24HOUR = 128; // 0x80
field public static final int FORMAT_ABBREV_ALL = 524288; // 0x80000
field public static final int FORMAT_ABBREV_MONTH = 65536; // 0x10000
field public static final int FORMAT_ABBREV_RELATIVE = 262144; // 0x40000
field public static final int FORMAT_ABBREV_TIME = 16384; // 0x4000
field public static final int FORMAT_ABBREV_WEEKDAY = 32768; // 0x8000
- field public static final int FORMAT_CAP_AMPM = 256; // 0x100
- field public static final int FORMAT_CAP_MIDNIGHT = 4096; // 0x1000
- field public static final int FORMAT_CAP_NOON = 1024; // 0x400
- field public static final int FORMAT_CAP_NOON_MIDNIGHT = 5120; // 0x1400
+ field public static final deprecated int FORMAT_CAP_AMPM = 256; // 0x100
+ field public static final deprecated int FORMAT_CAP_MIDNIGHT = 4096; // 0x1000
+ field public static final deprecated int FORMAT_CAP_NOON = 1024; // 0x400
+ field public static final deprecated int FORMAT_CAP_NOON_MIDNIGHT = 5120; // 0x1400
field public static final int FORMAT_NO_MIDNIGHT = 2048; // 0x800
field public static final int FORMAT_NO_MONTH_DAY = 32; // 0x20
field public static final int FORMAT_NO_NOON = 512; // 0x200
- field public static final int FORMAT_NO_NOON_MIDNIGHT = 2560; // 0xa00
+ field public static final deprecated int FORMAT_NO_NOON_MIDNIGHT = 2560; // 0xa00
field public static final int FORMAT_NO_YEAR = 8; // 0x8
field public static final int FORMAT_NUMERIC_DATE = 131072; // 0x20000
field public static final int FORMAT_SHOW_DATE = 16; // 0x10
@@ -21562,12 +21562,12 @@
field public static final int FORMAT_SHOW_YEAR = 4; // 0x4
field public static final deprecated int FORMAT_UTC = 8192; // 0x2000
field public static final long HOUR_IN_MILLIS = 3600000L; // 0x36ee80L
- field public static final java.lang.String HOUR_MINUTE_24 = "%H:%M";
- field public static final int LENGTH_LONG = 10; // 0xa
- field public static final int LENGTH_MEDIUM = 20; // 0x14
- field public static final int LENGTH_SHORT = 30; // 0x1e
- field public static final int LENGTH_SHORTER = 40; // 0x28
- field public static final int LENGTH_SHORTEST = 50; // 0x32
+ field public static final deprecated java.lang.String HOUR_MINUTE_24 = "%H:%M";
+ field public static final deprecated int LENGTH_LONG = 10; // 0xa
+ field public static final deprecated int LENGTH_MEDIUM = 20; // 0x14
+ field public static final deprecated int LENGTH_SHORT = 30; // 0x1e
+ field public static final deprecated int LENGTH_SHORTER = 40; // 0x28
+ field public static final deprecated int LENGTH_SHORTEST = 50; // 0x32
field public static final long MINUTE_IN_MILLIS = 60000L; // 0xea60L
field public static final java.lang.String MONTH_DAY_FORMAT = "%-d";
field public static final java.lang.String MONTH_FORMAT = "%B";
@@ -21578,8 +21578,8 @@
field public static final java.lang.String YEAR_FORMAT = "%Y";
field public static final java.lang.String YEAR_FORMAT_TWO_DIGITS = "%g";
field public static final long YEAR_IN_MILLIS = 31449600000L; // 0x7528ad000L
- field public static final int[] sameMonthTable;
- field public static final int[] sameYearTable;
+ field public static final deprecated int[] sameMonthTable;
+ field public static final deprecated int[] sameYearTable;
}
public final class Formatter {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 7242029..b7e0683a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4728,13 +4728,14 @@
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
- if (sMainThreadHandler == null) {
- sMainThreadHandler = new Handler();
- }
ActivityThread thread = new ActivityThread();
thread.attach(false);
+ if (sMainThreadHandler == null) {
+ sMainThreadHandler = thread.getHandler();
+ }
+
AsyncTask.init();
if (false) {
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index 226e107..7a9fc65 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -26,6 +26,7 @@
import android.accounts.Account;
import android.accounts.AccountAndUser;
+import android.content.res.Resources;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
@@ -337,6 +338,7 @@
private int mNextHistoryId = 0;
private SparseArray<Boolean> mMasterSyncAutomatically = new SparseArray<Boolean>();
+ private boolean mDefaultMasterSyncAutomatically;
private OnSyncRequestListener mSyncRequestListener;
@@ -346,6 +348,9 @@
mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));
+ mDefaultMasterSyncAutomatically = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_syncstorageengine_masterSyncAutomatically);
+
File systemDir = new File(dataDir, "system");
File syncDir = new File(systemDir, "sync");
syncDir.mkdirs();
@@ -781,7 +786,7 @@
public boolean getMasterSyncAutomatically(int userId) {
synchronized (mAuthorities) {
Boolean auto = mMasterSyncAutomatically.get(userId);
- return auto == null ? true : auto;
+ return auto == null ? mDefaultMasterSyncAutomatically : auto;
}
}
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 4d9077f..829620b 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -26,6 +26,7 @@
import android.os.Looper;
import android.os.Message;
import android.util.Log;
+import android.text.TextUtils;
import android.view.Surface;
import android.view.SurfaceHolder;
@@ -34,7 +35,6 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
-import java.util.StringTokenizer;
import java.util.concurrent.locks.ReentrantLock;
/**
@@ -1905,7 +1905,7 @@
private HashMap<String, String> mMap;
private Parameters() {
- mMap = new HashMap<String, String>();
+ mMap = new HashMap<String, String>(64);
}
/**
@@ -1929,7 +1929,7 @@
* semi-colon delimited key-value pairs
*/
public String flatten() {
- StringBuilder flattened = new StringBuilder();
+ StringBuilder flattened = new StringBuilder(128);
for (String k : mMap.keySet()) {
flattened.append(k);
flattened.append("=");
@@ -1952,9 +1952,9 @@
public void unflatten(String flattened) {
mMap.clear();
- StringTokenizer tokenizer = new StringTokenizer(flattened, ";");
- while (tokenizer.hasMoreElements()) {
- String kv = tokenizer.nextToken();
+ TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(';');
+ splitter.setString(flattened);
+ for (String kv : splitter) {
int pos = kv.indexOf('=');
if (pos == -1) {
continue;
@@ -3488,11 +3488,11 @@
private ArrayList<String> split(String str) {
if (str == null) return null;
- // Use StringTokenizer because it is faster than split.
- StringTokenizer tokenizer = new StringTokenizer(str, ",");
+ TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
+ splitter.setString(str);
ArrayList<String> substrings = new ArrayList<String>();
- while (tokenizer.hasMoreElements()) {
- substrings.add(tokenizer.nextToken());
+ for (String s : splitter) {
+ substrings.add(s);
}
return substrings;
}
@@ -3502,11 +3502,11 @@
private ArrayList<Integer> splitInt(String str) {
if (str == null) return null;
- StringTokenizer tokenizer = new StringTokenizer(str, ",");
+ TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
+ splitter.setString(str);
ArrayList<Integer> substrings = new ArrayList<Integer>();
- while (tokenizer.hasMoreElements()) {
- String token = tokenizer.nextToken();
- substrings.add(Integer.parseInt(token));
+ for (String s : splitter) {
+ substrings.add(Integer.parseInt(s));
}
if (substrings.size() == 0) return null;
return substrings;
@@ -3515,11 +3515,11 @@
private void splitInt(String str, int[] output) {
if (str == null) return;
- StringTokenizer tokenizer = new StringTokenizer(str, ",");
+ TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
+ splitter.setString(str);
int index = 0;
- while (tokenizer.hasMoreElements()) {
- String token = tokenizer.nextToken();
- output[index++] = Integer.parseInt(token);
+ for (String s : splitter) {
+ output[index++] = Integer.parseInt(s);
}
}
@@ -3527,11 +3527,11 @@
private void splitFloat(String str, float[] output) {
if (str == null) return;
- StringTokenizer tokenizer = new StringTokenizer(str, ",");
+ TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
+ splitter.setString(str);
int index = 0;
- while (tokenizer.hasMoreElements()) {
- String token = tokenizer.nextToken();
- output[index++] = Float.parseFloat(token);
+ for (String s : splitter) {
+ output[index++] = Float.parseFloat(s);
}
}
@@ -3558,10 +3558,11 @@
private ArrayList<Size> splitSize(String str) {
if (str == null) return null;
- StringTokenizer tokenizer = new StringTokenizer(str, ",");
+ TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
+ splitter.setString(str);
ArrayList<Size> sizeList = new ArrayList<Size>();
- while (tokenizer.hasMoreElements()) {
- Size size = strToSize(tokenizer.nextToken());
+ for (String s : splitter) {
+ Size size = strToSize(s);
if (size != null) sizeList.add(size);
}
if (sizeList.size() == 0) return null;
diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java
index 28bd289..0cc78c9 100644
--- a/core/java/android/net/EthernetDataTracker.java
+++ b/core/java/android/net/EthernetDataTracker.java
@@ -223,7 +223,7 @@
mIface = iface;
mNMService.setInterfaceUp(iface);
InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);
- mLinkUp = config.isActive();
+ mLinkUp = config.hasFlag("up");
if (config != null && mHwAddr == null) {
mHwAddr = config.getHardwareAddress();
if (mHwAddr != null) {
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 82cb74b..63c19ad 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -184,8 +184,17 @@
if (!TextUtils.equals(apnType, mApnType)) {
return;
}
- mNetworkInfo.setSubtype(TelephonyManager.getDefault().getNetworkType(),
- TelephonyManager.getDefault().getNetworkTypeName());
+
+ int oldSubtype = mNetworkInfo.getSubtype();
+ int newSubType = TelephonyManager.getDefault().getNetworkType();
+ String subTypeName = TelephonyManager.getDefault().getNetworkTypeName();
+ mNetworkInfo.setSubtype(newSubType, subTypeName);
+ if (newSubType != oldSubtype && mNetworkInfo.isConnected()) {
+ Message msg = mTarget.obtainMessage(EVENT_NETWORK_SUBTYPE_CHANGED,
+ oldSubtype, 0, mNetworkInfo);
+ msg.sendToTarget();
+ }
+
PhoneConstants.DataState state = Enum.valueOf(PhoneConstants.DataState.class,
intent.getStringExtra(PhoneConstants.STATE_KEY));
String reason = intent.getStringExtra(PhoneConstants.STATE_CHANGE_REASON_KEY);
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index 7df0193..0d6dcd6 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -69,6 +69,12 @@
public static final int EVENT_RESTORE_DEFAULT_NETWORK = 6;
/**
+ * msg.what = EVENT_NETWORK_SUBTYPE_CHANGED
+ * msg.obj = NetworkInfo object
+ */
+ public static final int EVENT_NETWORK_SUBTYPE_CHANGED = 7;
+
+ /**
* -------------------------------------------------------------
* Control Interface
* -------------------------------------------------------------
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index fb5263d..65d3f2b 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -329,7 +329,7 @@
throw new IllegalArgumentException("Bad address");
}
- mAddresses.append(String.format(" %s/%d", address.getHostAddress(), prefixLength));
+ mAddresses.append(' ' + address.getHostAddress() + '/' + prefixLength);
return this;
}
diff --git a/core/java/android/net/arp/ArpPeer.java b/core/java/android/net/arp/ArpPeer.java
index 8e666bc..6ba1e7c 100644
--- a/core/java/android/net/arp/ArpPeer.java
+++ b/core/java/android/net/arp/ArpPeer.java
@@ -53,9 +53,11 @@
mInterfaceName = interfaceName;
mMyAddr = myAddr;
- for (int i = 0; i < MAC_ADDR_LENGTH; i++) {
- mMyMac[i] = (byte) Integer.parseInt(mac.substring(
- i*3, (i*3) + 2), 16);
+ if (mac != null) {
+ for (int i = 0; i < MAC_ADDR_LENGTH; i++) {
+ mMyMac[i] = (byte) Integer.parseInt(mac.substring(
+ i*3, (i*3) + 2), 16);
+ }
}
if (myAddr instanceof Inet6Address || peer instanceof Inet6Address) {
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 7c103aa..0941d71 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -53,8 +53,6 @@
public static native int setPermissions(String file, int mode, int uid, int gid);
- public static native int setUMask(int mask);
-
/** returns the FAT file system volume ID for the volume mounted
* at the given mount point, or -1 for failure
* @param mountPoint point for FAT volume
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
old mode 100644
new mode 100755
index 8e123ac..e7b0579
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -8362,7 +8362,7 @@
// Line contains the query string - now search for it at the start of tokens.
List<String> lineTokens = new ArrayList<String>();
List<Integer> tokenOffsets = new ArrayList<Integer>();
- split(contentLine.trim(), lineTokens, tokenOffsets);
+ split(contentLine, lineTokens, tokenOffsets);
// As we find matches against the query, we'll populate this list with the marked
// (or unchanged) tokens.
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index da10311..2e962a0 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -161,12 +161,17 @@
public static final int FORMAT_NO_YEAR = 0x00008;
public static final int FORMAT_SHOW_DATE = 0x00010;
public static final int FORMAT_NO_MONTH_DAY = 0x00020;
+ @Deprecated
public static final int FORMAT_12HOUR = 0x00040;
+ @Deprecated
public static final int FORMAT_24HOUR = 0x00080;
+ @Deprecated
public static final int FORMAT_CAP_AMPM = 0x00100;
public static final int FORMAT_NO_NOON = 0x00200;
+ @Deprecated
public static final int FORMAT_CAP_NOON = 0x00400;
public static final int FORMAT_NO_MIDNIGHT = 0x00800;
+ @Deprecated
public static final int FORMAT_CAP_MIDNIGHT = 0x01000;
/**
* @deprecated Use
@@ -181,19 +186,25 @@
public static final int FORMAT_NUMERIC_DATE = 0x20000;
public static final int FORMAT_ABBREV_RELATIVE = 0x40000;
public static final int FORMAT_ABBREV_ALL = 0x80000;
+ @Deprecated
public static final int FORMAT_CAP_NOON_MIDNIGHT = (FORMAT_CAP_NOON | FORMAT_CAP_MIDNIGHT);
+ @Deprecated
public static final int FORMAT_NO_NOON_MIDNIGHT = (FORMAT_NO_NOON | FORMAT_NO_MIDNIGHT);
// Date and time format strings that are constant and don't need to be
// translated.
/**
* This is not actually the preferred 24-hour date format in all locales.
+ * @deprecated use {@link java.text.SimpleDateFormat} instead.
*/
+ @Deprecated
public static final String HOUR_MINUTE_24 = "%H:%M";
public static final String MONTH_FORMAT = "%B";
/**
* This is not actually a useful month name in all locales.
+ * @deprecated use {@link java.text.SimpleDateFormat} instead.
*/
+ @Deprecated
public static final String ABBREV_MONTH_FORMAT = "%b";
public static final String NUMERIC_MONTH_FORMAT = "%m";
public static final String MONTH_DAY_FORMAT = "%-d";
@@ -207,6 +218,7 @@
// The index is constructed from a bit-wise OR of the boolean values:
// {showTime, showYear, showWeekDay}. For example, if showYear and
// showWeekDay are both true, then the index would be 3.
+ /** @deprecated do not use. */
public static final int sameYearTable[] = {
com.android.internal.R.string.same_year_md1_md2,
com.android.internal.R.string.same_year_wday1_md1_wday2_md2,
@@ -233,6 +245,7 @@
// The index is constructed from a bit-wise OR of the boolean values:
// {showTime, showYear, showWeekDay}. For example, if showYear and
// showWeekDay are both true, then the index would be 3.
+ /** @deprecated do not use. */
public static final int sameMonthTable[] = {
com.android.internal.R.string.same_month_md1_md2,
com.android.internal.R.string.same_month_wday1_md1_wday2_md2,
@@ -259,7 +272,9 @@
*
* @more <p>
* e.g. "Sunday" or "January"
+ * @deprecated use {@link java.text.SimpleDateFormat} instead.
*/
+ @Deprecated
public static final int LENGTH_LONG = 10;
/**
@@ -268,7 +283,9 @@
*
* @more <p>
* e.g. "Sun" or "Jan"
+ * @deprecated use {@link java.text.SimpleDateFormat} instead.
*/
+ @Deprecated
public static final int LENGTH_MEDIUM = 20;
/**
@@ -278,14 +295,18 @@
* <p>e.g. "Su" or "Jan"
* <p>In most languages, the results returned for LENGTH_SHORT will be the same as
* the results returned for {@link #LENGTH_MEDIUM}.
+ * @deprecated use {@link java.text.SimpleDateFormat} instead.
*/
+ @Deprecated
public static final int LENGTH_SHORT = 30;
/**
* Request an even shorter abbreviated version of the name.
* Do not use this. Currently this will always return the same result
* as {@link #LENGTH_SHORT}.
+ * @deprecated use {@link java.text.SimpleDateFormat} instead.
*/
+ @Deprecated
public static final int LENGTH_SHORTER = 40;
/**
@@ -295,7 +316,9 @@
* <p>e.g. "S", "T", "T" or "J"
* <p>In some languages, the results returned for LENGTH_SHORTEST will be the same as
* the results returned for {@link #LENGTH_SHORT}.
+ * @deprecated use {@link java.text.SimpleDateFormat} instead.
*/
+ @Deprecated
public static final int LENGTH_SHORTEST = 50;
/**
@@ -309,7 +332,9 @@
* Undefined lengths will return {@link #LENGTH_MEDIUM}
* but may return something different in the future.
* @throws IndexOutOfBoundsException if the dayOfWeek is out of bounds.
+ * @deprecated use {@link java.text.SimpleDateFormat} instead.
*/
+ @Deprecated
public static String getDayOfWeekString(int dayOfWeek, int abbrev) {
int[] list;
switch (abbrev) {
@@ -330,7 +355,9 @@
* @param ampm Either {@link Calendar#AM Calendar.AM} or {@link Calendar#PM Calendar.PM}.
* @throws IndexOutOfBoundsException if the ampm is out of bounds.
* @return Localized version of "AM" or "PM".
+ * @deprecated use {@link java.text.SimpleDateFormat} instead.
*/
+ @Deprecated
public static String getAMPMString(int ampm) {
Resources r = Resources.getSystem();
return r.getString(sAmPm[ampm - Calendar.AM]);
@@ -345,7 +372,9 @@
* Undefined lengths will return {@link #LENGTH_MEDIUM}
* but may return something different in the future.
* @return Localized month of the year.
+ * @deprecated use {@link java.text.SimpleDateFormat} instead.
*/
+ @Deprecated
public static String getMonthString(int month, int abbrev) {
// Note that here we use sMonthsMedium for MEDIUM, SHORT and SHORTER.
// This is a shortcut to not spam the translators with too many variations
@@ -378,7 +407,9 @@
* but may return something different in the future.
* @return Localized month of the year.
* @hide Pending API council approval
+ * @deprecated use {@link java.text.SimpleDateFormat} instead.
*/
+ @Deprecated
public static String getStandaloneMonthString(int month, int abbrev) {
// Note that here we use sMonthsMedium for MEDIUM, SHORT and SHORTER.
// This is a shortcut to not spam the translators with too many variations
@@ -1618,7 +1649,7 @@
String result;
long now = System.currentTimeMillis();
- long span = now - millis;
+ long span = Math.abs(now - millis);
synchronized (DateUtils.class) {
if (sNowTime == null) {
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 84a6129..34c08ef 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -686,6 +686,10 @@
// It's used to dismiss the dialog in destroy if not done before.
private AlertDialog mListBoxDialog = null;
+ // Reference to the save password dialog so it can be dimissed in
+ // destroy if not done before.
+ private AlertDialog mSavePasswordDialog = null;
+
static final String LOGTAG = "webview";
private ZoomManager mZoomManager;
@@ -1811,7 +1815,7 @@
neverRemember.getData().putString("password", password);
neverRemember.obj = resumeMsg;
- new AlertDialog.Builder(mContext)
+ mSavePasswordDialog = new AlertDialog.Builder(mContext)
.setTitle(com.android.internal.R.string.save_password_label)
.setMessage(com.android.internal.R.string.save_password_message)
.setPositiveButton(com.android.internal.R.string.save_password_notnow,
@@ -1822,6 +1826,7 @@
resumeMsg.sendToTarget();
mResumeMsg = null;
}
+ mSavePasswordDialog = null;
}
})
.setNeutralButton(com.android.internal.R.string.save_password_remember,
@@ -1832,6 +1837,7 @@
remember.sendToTarget();
mResumeMsg = null;
}
+ mSavePasswordDialog = null;
}
})
.setNegativeButton(com.android.internal.R.string.save_password_never,
@@ -1842,6 +1848,7 @@
neverRemember.sendToTarget();
mResumeMsg = null;
}
+ mSavePasswordDialog = null;
}
})
.setOnCancelListener(new OnCancelListener() {
@@ -1851,6 +1858,7 @@
resumeMsg.sendToTarget();
mResumeMsg = null;
}
+ mSavePasswordDialog = null;
}
}).show();
// Return true so that WebViewCore will pause while the dialog is
@@ -2090,6 +2098,10 @@
mListBoxDialog.dismiss();
mListBoxDialog = null;
}
+ if (mSavePasswordDialog != null) {
+ mSavePasswordDialog.dismiss();
+ mSavePasswordDialog = null;
+ }
if (mWebViewCore != null) {
// Tell WebViewCore to destroy itself
synchronized (this) {
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 18c4fe6..ff0579c 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -22,6 +22,7 @@
import android.graphics.Rect;
import android.os.Bundle;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.FocusFinder;
import android.view.InputDevice;
import android.view.KeyEvent;
@@ -62,6 +63,7 @@
private static final float MAX_SCROLL_FACTOR = ScrollView.MAX_SCROLL_FACTOR;
+ private static final String TAG = "HorizontalScrollView";
private long mLastScroll;
@@ -456,6 +458,12 @@
}
final int pointerIndex = ev.findPointerIndex(activePointerId);
+ if (pointerIndex == -1) {
+ Log.e(TAG, "Invalid pointerId=" + activePointerId
+ + " in onInterceptTouchEvent");
+ break;
+ }
+
final int x = (int) ev.getX(pointerIndex);
final int xDiff = (int) Math.abs(x - mLastMotionX);
if (xDiff > mTouchSlop) {
@@ -557,6 +565,11 @@
}
case MotionEvent.ACTION_MOVE:
final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (activePointerIndex == -1) {
+ Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
+ break;
+ }
+
final int x = (int) ev.getX(activePointerIndex);
int deltaX = mLastMotionX - x;
if (!mIsBeingDragged && Math.abs(deltaX) > mTouchSlop) {
diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java
index fc35f05..f76ab2b 100644
--- a/core/java/android/widget/MediaController.java
+++ b/core/java/android/widget/MediaController.java
@@ -477,7 +477,8 @@
return true;
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
|| keyCode == KeyEvent.KEYCODE_VOLUME_UP
- || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
+ || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE
+ || keyCode == KeyEvent.KEYCODE_CAMERA) {
// don't show the controls for volume adjustment
return super.dispatchKeyEvent(event);
} else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU) {
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index ebc54f4..8747dc3 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -25,6 +25,7 @@
import android.os.Bundle;
import android.os.StrictMode;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.FocusFinder;
import android.view.InputDevice;
import android.view.KeyEvent;
@@ -69,6 +70,8 @@
static final float MAX_SCROLL_FACTOR = 0.5f;
+ private static final String TAG = "ScrollView";
+
private long mLastScroll;
private final Rect mTempRect = new Rect();
@@ -478,6 +481,12 @@
}
final int pointerIndex = ev.findPointerIndex(activePointerId);
+ if (pointerIndex == -1) {
+ Log.e(TAG, "Invalid pointerId=" + activePointerId
+ + " in onInterceptTouchEvent");
+ break;
+ }
+
final int y = (int) ev.getY(pointerIndex);
final int yDiff = Math.abs(y - mLastMotionY);
if (yDiff > mTouchSlop) {
@@ -585,6 +594,11 @@
}
case MotionEvent.ACTION_MOVE:
final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (activePointerIndex == -1) {
+ Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
+ break;
+ }
+
final int y = (int) ev.getY(activePointerIndex);
int deltaY = mLastMotionY - y;
if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) {
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index c44ce8a..86433d4 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -1506,6 +1506,9 @@
// because the voice search activity will always need to insert "QUERY" into
// it anyway.
Bundle queryExtras = new Bundle();
+ if (mAppSearchData != null) {
+ queryExtras.putParcelable(SearchManager.APP_DATA, mAppSearchData);
+ }
// Now build the intent to launch the voice search. Add all necessary
// extras to launch the voice recognizer, and then all the necessary extras
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 471f259..9ea1372 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -675,7 +675,7 @@
@Override
public void setChecked(boolean checked) {
super.setChecked(checked);
- mThumbPosition = checked ? getThumbScrollRange() : 0;
+ mThumbPosition = isChecked() ? getThumbScrollRange() : 0;
invalidate();
}
diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java
index 8bb9348..238dc55 100644
--- a/core/java/android/widget/TabHost.java
+++ b/core/java/android/widget/TabHost.java
@@ -48,6 +48,10 @@
*/
public class TabHost extends FrameLayout implements ViewTreeObserver.OnTouchModeChangeListener {
+ private static final int TABWIDGET_LOCATION_LEFT = 0;
+ private static final int TABWIDGET_LOCATION_TOP = 1;
+ private static final int TABWIDGET_LOCATION_RIGHT = 2;
+ private static final int TABWIDGET_LOCATION_BOTTOM = 3;
private TabWidget mTabWidget;
private FrameLayout mTabContent;
private List<TabSpec> mTabSpecs = new ArrayList<TabSpec>(2);
@@ -293,22 +297,73 @@
return mTabContent;
}
+ /**
+ * Get the location of the TabWidget.
+ *
+ * @return The TabWidget location.
+ */
+ private int getTabWidgetLocation() {
+ int location = TABWIDGET_LOCATION_TOP;
+
+ switch (mTabWidget.getOrientation()) {
+ case LinearLayout.VERTICAL:
+ location = (mTabContent.getLeft() < mTabWidget.getLeft()) ? TABWIDGET_LOCATION_RIGHT
+ : TABWIDGET_LOCATION_LEFT;
+ break;
+ case LinearLayout.HORIZONTAL:
+ default:
+ location = (mTabContent.getTop() < mTabWidget.getTop()) ? TABWIDGET_LOCATION_BOTTOM
+ : TABWIDGET_LOCATION_TOP;
+ break;
+ }
+ return location;
+ }
+
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
final boolean handled = super.dispatchKeyEvent(event);
- // unhandled key ups change focus to tab indicator for embedded activities
- // when there is nothing that will take focus from default focus searching
+ // unhandled key events change focus to tab indicator for embedded
+ // activities when there is nothing that will take focus from default
+ // focus searching
if (!handled
&& (event.getAction() == KeyEvent.ACTION_DOWN)
- && (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP)
&& (mCurrentView != null)
&& (mCurrentView.isRootNamespace())
- && (mCurrentView.hasFocus())
- && (mCurrentView.findFocus().focusSearch(View.FOCUS_UP) == null)) {
- mTabWidget.getChildTabViewAt(mCurrentTab).requestFocus();
- playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
- return true;
+ && (mCurrentView.hasFocus())) {
+ int keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_UP;
+ int directionShouldChangeFocus = View.FOCUS_UP;
+ int soundEffect = SoundEffectConstants.NAVIGATION_UP;
+
+ switch (getTabWidgetLocation()) {
+ case TABWIDGET_LOCATION_LEFT:
+ keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_LEFT;
+ directionShouldChangeFocus = View.FOCUS_LEFT;
+ soundEffect = SoundEffectConstants.NAVIGATION_LEFT;
+ break;
+ case TABWIDGET_LOCATION_RIGHT:
+ keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_RIGHT;
+ directionShouldChangeFocus = View.FOCUS_RIGHT;
+ soundEffect = SoundEffectConstants.NAVIGATION_RIGHT;
+ break;
+ case TABWIDGET_LOCATION_BOTTOM:
+ keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_DOWN;
+ directionShouldChangeFocus = View.FOCUS_DOWN;
+ soundEffect = SoundEffectConstants.NAVIGATION_DOWN;
+ break;
+ case TABWIDGET_LOCATION_TOP:
+ default:
+ keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_UP;
+ directionShouldChangeFocus = View.FOCUS_UP;
+ soundEffect = SoundEffectConstants.NAVIGATION_UP;
+ break;
+ }
+ if (event.getKeyCode() == keyCodeShouldChangeFocus
+ && mCurrentView.findFocus().focusSearch(directionShouldChangeFocus) == null) {
+ mTabWidget.getChildTabViewAt(mCurrentTab).requestFocus();
+ playSoundEffect(soundEffect);
+ return true;
+ }
}
return handled;
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 998c037..4924326 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -16,16 +16,17 @@
package com.android.internal.os;
+import static libcore.io.OsConstants.S_IRWXG;
+import static libcore.io.OsConstants.S_IRWXO;
+
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.net.LocalServerSocket;
import android.os.Debug;
-import android.os.FileUtils;
import android.os.Process;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.util.EventLog;
import android.util.Log;
@@ -33,6 +34,7 @@
import dalvik.system.Zygote;
import libcore.io.IoUtils;
+import libcore.io.Libcore;
import java.io.BufferedReader;
import java.io.FileDescriptor;
@@ -447,7 +449,7 @@
closeServerSocket();
// set umask to 0077 so new files and directories will default to owner-only permissions.
- FileUtils.setUMask(FileUtils.S_IRWXG | FileUtils.S_IRWXO);
+ Libcore.os.umask(S_IRWXG | S_IRWXO);
if (parsedArgs.niceName != null) {
Process.setArgV0(parsedArgs.niceName);
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 3c27caf..1bba5b4 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -298,8 +298,18 @@
}
bool success = false;
- SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
- if (NULL != strm) {
+ if (NULL != bitmap) {
+ SkAutoLockPixels alp(*bitmap);
+
+ if (NULL == bitmap->getPixels()) {
+ return false;
+ }
+
+ SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
+ if (NULL == strm) {
+ return false;
+ }
+
SkImageEncoder* encoder = SkImageEncoder::Create(fm);
if (NULL != encoder) {
success = encoder->encodeStream(strm, *bitmap, quality);
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index a65262c..a4d5477 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -750,7 +750,7 @@
static void doTextBounds(JNIEnv* env, const jchar* text, int count,
jobject bounds, const SkPaint& paint)
{
- SkRect r;
+ SkRect r{0,0,0,0};
SkIRect ir;
sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(&paint,
diff --git a/core/jni/android_os_FileUtils.cpp b/core/jni/android_os_FileUtils.cpp
index 82bfc36..a07f5b7 100644
--- a/core/jni/android_os_FileUtils.cpp
+++ b/core/jni/android_os_FileUtils.cpp
@@ -55,11 +55,6 @@
return chmod(file8.string(), mode) == 0 ? 0 : errno;
}
-jint android_os_FileUtils_setUMask(JNIEnv* env, jobject clazz, jint mask)
-{
- return umask(mask);
-}
-
jint android_os_FileUtils_getFatVolumeId(JNIEnv* env, jobject clazz, jstring path)
{
if (path == NULL) {
@@ -83,7 +78,6 @@
static const JNINativeMethod methods[] = {
{"setPermissions", "(Ljava/lang/String;III)I", (void*)android_os_FileUtils_setPermissions},
- {"setUMask", "(I)I", (void*)android_os_FileUtils_setUMask},
{"getFatVolumeId", "(Ljava/lang/String;)I", (void*)android_os_FileUtils_getFatVolumeId},
};
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ccc85a3..0a0c81c 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -865,4 +865,7 @@
<!-- Set to true to add links to Cell Broadcast app from Settings and MMS app. -->
<bool name="config_cellBroadcastAppLinks">false</bool>
+
+ <!-- The default value if the SyncStorageEngine should sync automatically or not -->
+ <bool name="config_syncstorageengine_masterSyncAutomatically">true</bool>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 40768a0..6334f88 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -253,6 +253,7 @@
<java-symbol type="bool" name="config_sms_capable" />
<java-symbol type="bool" name="config_sms_utf8_support" />
<java-symbol type="bool" name="config_swipeDisambiguation" />
+ <java-symbol type="bool" name="config_syncstorageengine_masterSyncAutomatically" />
<java-symbol type="bool" name="config_telephony_use_own_number_for_voicemail" />
<java-symbol type="bool" name="config_ui_enableFadingMarquee" />
<java-symbol type="bool" name="config_use_strict_phone_number_comparation" />
diff --git a/data/etc/Android.mk b/data/etc/Android.mk
index 71a9a15..134ac0c 100644
--- a/data/etc/Android.mk
+++ b/data/etc/Android.mk
@@ -21,8 +21,6 @@
LOCAL_MODULE := platform.xml
-LOCAL_MODULE_TAGS := user
-
LOCAL_MODULE_CLASS := ETC
# This will install the file in /system/etc/permissions
@@ -38,8 +36,6 @@
#LOCAL_MODULE := required_hardware.xml
-#LOCAL_MODULE_TAGS := user
-
#LOCAL_MODULE_CLASS := ETC
# This will install the file in /system/etc/permissions
diff --git a/drm/jni/Android.mk b/drm/jni/Android.mk
index f8ecc8c..fff7eee 100644
--- a/drm/jni/Android.mk
+++ b/drm/jni/Android.mk
@@ -35,7 +35,8 @@
$(JNI_H_INCLUDE) \
$(TOP)/frameworks/av/drm/libdrmframework/include \
$(TOP)/frameworks/av/drm/libdrmframework/plugins/common/include \
- $(TOP)/frameworks/av/include
+ $(TOP)/frameworks/av/include \
+ $(TOP)/libcore/include
diff --git a/drm/jni/android_drm_DrmManagerClient.cpp b/drm/jni/android_drm_DrmManagerClient.cpp
index 14ec4d6..fb685a2 100644
--- a/drm/jni/android_drm_DrmManagerClient.cpp
+++ b/drm/jni/android_drm_DrmManagerClient.cpp
@@ -20,6 +20,7 @@
#include <jni.h>
#include <JNIHelp.h>
+#include <ScopedLocalRef.h>
#include <android_runtime/AndroidRuntime.h>
#include <drm/DrmInfo.h>
@@ -250,16 +251,18 @@
= getDrmManagerClientImpl(env, thiz)->getConstraints(uniqueId, &pathString, usage);
jclass localRef = env->FindClass("android/content/ContentValues");
+ jmethodID ContentValues_putByteArray =
+ env->GetMethodID(localRef, "put", "(Ljava/lang/String;[B)V");
+ jmethodID ContentValues_putString =
+ env->GetMethodID(localRef, "put", "(Ljava/lang/String;Ljava/lang/String;)V");
+ jmethodID ContentValues_constructor = env->GetMethodID(localRef, "<init>", "()V");
jobject constraints = NULL;
if (NULL != localRef && NULL != pConstraints) {
- // Get the constructor id
- jmethodID constructorId = env->GetMethodID(localRef, "<init>", "()V");
// create the java DrmConstraints object
- constraints = env->NewObject(localRef, constructorId);
+ constraints = env->NewObject(localRef, ContentValues_constructor);
DrmConstraints::KeyIterator keyIt = pConstraints->keyIterator();
-
while (keyIt.hasNext()) {
String8 key = keyIt.next();
@@ -267,18 +270,18 @@
if (DrmConstraints::EXTENDED_METADATA == key) {
const char* value = pConstraints->getAsByteArray(&key);
if (NULL != value) {
- jbyteArray dataArray = env->NewByteArray(strlen(value));
- env->SetByteArrayRegion(dataArray, 0, strlen(value), (jbyte*)value);
- env->CallVoidMethod(
- constraints, env->GetMethodID(localRef, "put", "(Ljava/lang/String;[B)V"),
- env->NewStringUTF(key.string()), dataArray);
+ ScopedLocalRef<jbyteArray> dataArray(env, env->NewByteArray(strlen(value)));
+ ScopedLocalRef<jstring> keyString(env, env->NewStringUTF(key.string()));
+ env->SetByteArrayRegion(dataArray.get(), 0, strlen(value), (jbyte*)value);
+ env->CallVoidMethod(constraints, ContentValues_putByteArray,
+ keyString.get(), dataArray.get());
}
} else {
String8 value = pConstraints->get(key);
- env->CallVoidMethod(
- constraints,
- env->GetMethodID(localRef, "put", "(Ljava/lang/String;Ljava/lang/String;)V"),
- env->NewStringUTF(key.string()), env->NewStringUTF(value.string()));
+ ScopedLocalRef<jstring> keyString(env, env->NewStringUTF(key.string()));
+ ScopedLocalRef<jstring> valueString(env, env->NewStringUTF(value.string()));
+ env->CallVoidMethod(constraints, ContentValues_putString,
+ keyString.get(), valueString.get());
}
}
}
@@ -297,8 +300,10 @@
jobject metadata = NULL;
- jclass localRef = NULL;
- localRef = env->FindClass("android/content/ContentValues");
+ jclass localRef = env->FindClass("android/content/ContentValues");
+ jmethodID ContentValues_putString =
+ env->GetMethodID(localRef, "put", "(Ljava/lang/String;Ljava/lang/String;)V");
+
if (NULL != localRef && NULL != pMetadata) {
// Get the constructor id
jmethodID constructorId = NULL;
@@ -313,9 +318,10 @@
// insert the entry<constraintKey, constraintValue>
// to newly created java object
String8 value = pMetadata->get(key);
- env->CallVoidMethod(metadata, env->GetMethodID(localRef, "put",
- "(Ljava/lang/String;Ljava/lang/String;)V"),
- env->NewStringUTF(key.string()), env->NewStringUTF(value.string()));
+ ScopedLocalRef<jstring> keyString(env, env->NewStringUTF(key.string()));
+ ScopedLocalRef<jstring> valueString(env, env->NewStringUTF(value.string()));
+ env->CallVoidMethod(metadata, ContentValues_putString,
+ keyString.get(), valueString.get());
}
}
}
@@ -426,29 +432,30 @@
DrmInfo drmInfo(mInfoType, buffer, mMimeType);
jclass clazz = env->FindClass("android/drm/DrmInfo");
+ jmethodID DrmInfo_get = env->GetMethodID(clazz, "get", "(Ljava/lang/String;)Ljava/lang/Object;");
jobject keyIterator
= env->CallObjectMethod(drmInfoObject,
env->GetMethodID(clazz, "keyIterator", "()Ljava/util/Iterator;"));
- jmethodID hasNextId = env->GetMethodID(env->FindClass("java/util/Iterator"), "hasNext", "()Z");
+ jclass Iterator_class = env->FindClass("java/util/Iterator");
+ jmethodID Iterator_hasNext = env->GetMethodID(Iterator_class, "hasNext", "()Z");
+ jmethodID Iterator_next = env->GetMethodID(Iterator_class, "next", "()Ljava/lang/Object;");
- while (env->CallBooleanMethod(keyIterator, hasNextId)) {
- jstring key = (jstring) env->CallObjectMethod(keyIterator,
- env->GetMethodID(env->FindClass("java/util/Iterator"),
- "next", "()Ljava/lang/Object;"));
+ jclass Object_class = env->FindClass("java/lang/Object");
+ jmethodID Object_toString = env->GetMethodID(Object_class, "toString", "()Ljava/lang/String;");
- jobject valueObject = env->CallObjectMethod(drmInfoObject,
- env->GetMethodID(clazz, "get", "(Ljava/lang/String;)Ljava/lang/Object;"), key);
-
- jstring valString = NULL;
- if (NULL != valueObject) {
- valString = (jstring) env->CallObjectMethod(valueObject,
- env->GetMethodID(env->FindClass("java/lang/Object"),
- "toString", "()Ljava/lang/String;"));
+ while (env->CallBooleanMethod(keyIterator, Iterator_hasNext)) {
+ ScopedLocalRef<jstring> key(env,
+ (jstring) env->CallObjectMethod(keyIterator, Iterator_next));
+ ScopedLocalRef<jobject> valueObject(env,
+ env->CallObjectMethod(drmInfoObject, DrmInfo_get, key.get()));
+ ScopedLocalRef<jstring> valString(env, NULL);
+ if (NULL != valueObject.get()) {
+ valString.reset((jstring) env->CallObjectMethod(valueObject.get(), Object_toString));
}
- String8 keyString = Utility::getStringValue(env, key);
- String8 valueString = Utility::getStringValue(env, valString);
+ String8 keyString = Utility::getStringValue(env, key.get());
+ String8 valueString = Utility::getStringValue(env, valString.get());
ALOGV("Key: %s | Value: %s", keyString.string(), valueString.string());
drmInfo.put(keyString, valueString);
@@ -508,20 +515,21 @@
jobject keyIterator
= env->CallObjectMethod(drmInfoRequest,
env->GetMethodID(clazz, "keyIterator", "()Ljava/util/Iterator;"));
+ jmethodID DrmInfoRequest_get = env->GetMethodID(clazz,
+ "get", "(Ljava/lang/String;)Ljava/lang/Object;");
- jmethodID hasNextId = env->GetMethodID(env->FindClass("java/util/Iterator"), "hasNext", "()Z");
+ jclass Iterator_class = env->FindClass("java/util/Iterator");
+ jmethodID Iterator_hasNext = env->GetMethodID(Iterator_class, "hasNext", "()Z");
+ jmethodID Iterator_next = env->GetMethodID(Iterator_class, "next", "()Ljava/lang/Object;");
- while (env->CallBooleanMethod(keyIterator, hasNextId)) {
- jstring key
- = (jstring) env->CallObjectMethod(keyIterator,
- env->GetMethodID(env->FindClass("java/util/Iterator"),
- "next", "()Ljava/lang/Object;"));
+ while (env->CallBooleanMethod(keyIterator, Iterator_hasNext)) {
+ ScopedLocalRef<jstring> key(env,
+ (jstring) env->CallObjectMethod(keyIterator, Iterator_next));
+ ScopedLocalRef<jstring> value(env,
+ (jstring) env->CallObjectMethod(drmInfoRequest, DrmInfoRequest_get, key.get()));
- jstring value = (jstring) env->CallObjectMethod(drmInfoRequest,
- env->GetMethodID(clazz, "get", "(Ljava/lang/String;)Ljava/lang/Object;"), key);
-
- String8 keyString = Utility::getStringValue(env, key);
- String8 valueString = Utility::getStringValue(env, value);
+ String8 keyString = Utility::getStringValue(env, key.get());
+ String8 valueString = Utility::getStringValue(env, value.get());
ALOGV("Key: %s | Value: %s", keyString.string(), valueString.string());
drmInfoReq.put(keyString, valueString);
@@ -552,9 +560,10 @@
while (it.hasNext()) {
String8 key = it.next();
String8 value = pDrmInfo->get(key);
-
+ ScopedLocalRef<jstring> keyString(env, env->NewStringUTF(key.string()));
+ ScopedLocalRef<jstring> valueString(env, env->NewStringUTF(value.string()));
env->CallVoidMethod(drmInfoObject, putMethodId,
- env->NewStringUTF(key.string()), env->NewStringUTF(value.string()));
+ keyString.get(), valueString.get());
}
}
delete [] pDrmInfo->getData().data;
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 3d5d1a9..b205a77 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -718,7 +718,7 @@
LOG_API("nAllocationRead_i, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
jint *ptr = _env->GetIntArrayElements(data, NULL);
jsize length = _env->GetArrayLength(data);
- rsAllocationRead(con, (RsAllocation)alloc, ptr, length);
+ rsAllocationRead(con, (RsAllocation)alloc, ptr, length * sizeof(int));
_env->ReleaseIntArrayElements(data, ptr, 0);
}
@@ -729,7 +729,7 @@
LOG_API("nAllocationRead_i, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
jshort *ptr = _env->GetShortArrayElements(data, NULL);
jsize length = _env->GetArrayLength(data);
- rsAllocationRead(con, (RsAllocation)alloc, ptr, length);
+ rsAllocationRead(con, (RsAllocation)alloc, ptr, length * sizeof(short));
_env->ReleaseShortArrayElements(data, ptr, 0);
}
@@ -740,7 +740,7 @@
LOG_API("nAllocationRead_i, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
jbyte *ptr = _env->GetByteArrayElements(data, NULL);
jsize length = _env->GetArrayLength(data);
- rsAllocationRead(con, (RsAllocation)alloc, ptr, length);
+ rsAllocationRead(con, (RsAllocation)alloc, ptr, length * sizeof(char));
_env->ReleaseByteArrayElements(data, ptr, 0);
}
@@ -751,7 +751,7 @@
LOG_API("nAllocationRead_f, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
jsize length = _env->GetArrayLength(data);
- rsAllocationRead(con, (RsAllocation)alloc, ptr, length);
+ rsAllocationRead(con, (RsAllocation)alloc, ptr, length * sizeof(float));
_env->ReleaseFloatArrayElements(data, ptr, 0);
}
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 5fdbc11..b51b1e1 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -617,7 +617,7 @@
uint32_t* retOriginX, uint32_t* retOriginY) {
cachedGlyph->mIsValid = false;
// If the glyph is too tall, don't cache it
- if (glyph.fHeight + TEXTURE_BORDER_SIZE > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
+ if (mCacheLines.size() != 0 && (glyph.fHeight + TEXTURE_BORDER_SIZE > mCacheLines[mCacheLines.size() - 1]->mMaxHeight)) {
ALOGE("Font size to large to fit in cache. width, height = %i, %i",
(int) glyph.fWidth, (int) glyph.fHeight);
return;
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 9af201d..613354f 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -351,12 +351,11 @@
*/
public void setCaptureRate(double fps) {
// Make sure that time lapse is enabled when this method is called.
- setParameter(String.format("time-lapse-enable=1"));
+ setParameter("time-lapse-enable=1");
double timeBetweenFrameCapture = 1 / fps;
int timeBetweenFrameCaptureMs = (int) (1000 * timeBetweenFrameCapture);
- setParameter(String.format("time-between-time-lapse-frame-capture=%d",
- timeBetweenFrameCaptureMs));
+ setParameter("time-between-time-lapse-frame-capture=" + timeBetweenFrameCaptureMs);
}
/**
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 6f8b809..fd37bcf 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -1399,7 +1399,8 @@
long lastModifiedSeconds = file.lastModified() / 1000;
if (!MediaFile.isAudioFileType(fileType) && !MediaFile.isVideoFileType(fileType) &&
- !MediaFile.isImageFileType(fileType) && !MediaFile.isPlayListFileType(fileType)) {
+ !MediaFile.isImageFileType(fileType) && !MediaFile.isPlayListFileType(fileType) &&
+ !MediaFile.isDrmFileType(fileType)) {
// no need to use the media scanner, but we need to update last modified and file size
ContentValues values = new ContentValues();
diff --git a/media/jni/mediaeditor/VideoEditorClasses.cpp b/media/jni/mediaeditor/VideoEditorClasses.cpp
index 4e0e0f2..4982a47 100755
--- a/media/jni/mediaeditor/VideoEditorClasses.cpp
+++ b/media/jni/mediaeditor/VideoEditorClasses.cpp
@@ -1853,6 +1853,9 @@
// Get the clip settings.
videoEditClasses_getClipSettings(pResult, pEnv, clipSettings,
&pSettings->pClipList[i]);
+
+ // Free the local references to avoid memory leaks
+ pEnv->DeleteLocalRef(clipSettings);
}
}
}
@@ -1877,6 +1880,9 @@
// Get the transition settings.
videoEditClasses_getTransitionSettings(pResult, pEnv,
transitionSettings, &pSettings->pTransitionList[i]);
+
+ // Free the local references to avoid memory leaks
+ pEnv->DeleteLocalRef(transitionSettings);
}
}
}
@@ -1900,6 +1906,9 @@
// Get the effect settings.
videoEditClasses_getEffectSettings(pResult, pEnv, effectSettings,
&pSettings->Effects[i]);
+
+ // Free the local references to avoid memory leaks
+ pEnv->DeleteLocalRef(effectSettings);
}
}
}
diff --git a/media/jni/mediaeditor/VideoEditorJava.cpp b/media/jni/mediaeditor/VideoEditorJava.cpp
index ec8050f..bcf9099 100755
--- a/media/jni/mediaeditor/VideoEditorJava.cpp
+++ b/media/jni/mediaeditor/VideoEditorJava.cpp
@@ -387,6 +387,9 @@
(*pLength) = length;
}
}
+
+ // Delete local references to avoid memory leaks
+ pEnv->DeleteLocalRef(string);
}
// Return the string.
diff --git a/media/jni/mediaeditor/VideoEditorMain.cpp b/media/jni/mediaeditor/VideoEditorMain.cpp
index 41ec120..41c28c0 100755
--- a/media/jni/mediaeditor/VideoEditorMain.cpp
+++ b/media/jni/mediaeditor/VideoEditorMain.cpp
@@ -380,6 +380,9 @@
pEnv->GetIntField(object,fid);
M4OSA_TRACE1_1("videoRotation = %d",
pSettings->ClipProperties.videoRotationDegrees);
+
+ // Free the local references to avoid memory leaks
+ pEnv->DeleteLocalRef(clazz);
}
static void jniPreviewProgressCallback (void* cookie, M4OSA_UInt32 msgType,
@@ -1849,7 +1852,9 @@
"not initialized");
if (needToBeLoaded) {
getClipSetting(pEnv,properties, pContext->pEditSettings->pClipList[i]);
+ pEnv->DeleteLocalRef(properties);
} else {
+ pEnv->DeleteLocalRef(properties);
goto videoEditor_populateSettings_cleanup;
}
}
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index cd0da5a..2c9a6fe 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -46,6 +46,8 @@
<bool name="def_netstats_enabled">true</bool>
<bool name="def_usb_mass_storage_enabled">true</bool>
<bool name="def_wifi_on">false</bool>
+ <!-- 0 == default, 1 == never while plugged, 2 == never -->
+ <integer name="def_wifi_sleep_policy">0</integer>
<bool name="def_networks_available_notification_on">true</bool>
<bool name="def_backup_enabled">false</bool>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index de078d20..aa2164e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -1496,6 +1496,8 @@
loadIntegerSetting(stmt, Settings.System.POINTER_SPEED,
R.integer.def_pointer_speed);
+ loadIntegerSetting(stmt, Settings.System.WIFI_SLEEP_POLICY,
+ R.integer.def_wifi_sleep_policy);
} finally {
if (stmt != null) stmt.close();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 751764c..22a8443 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -976,6 +976,8 @@
combinedActivityIconId = mMobileActivityIconId;
combinedSignalIconId = mDataSignalIconId; // set by updateDataIcon()
mContentDescriptionCombinedSignal = mContentDescriptionDataType;
+ } else {
+ mMobileActivityIconId = 0;
}
}
diff --git a/services/common_time/common_clock_service.h b/services/common_time/common_clock_service.h
index a65e398..bd663f0 100644
--- a/services/common_time/common_clock_service.h
+++ b/services/common_time/common_clock_service.h
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-#include <common_time/ICommonClock.h>
-
#ifndef ANDROID_COMMON_CLOCK_SERVICE_H
#define ANDROID_COMMON_CLOCK_SERVICE_H
+#include <sys/socket.h>
+#include <common_time/ICommonClock.h>
+
namespace android {
class CommonTimeServer;
diff --git a/services/common_time/common_time_config_service.h b/services/common_time/common_time_config_service.h
index 8283c24..89806dd 100644
--- a/services/common_time/common_time_config_service.h
+++ b/services/common_time/common_time_config_service.h
@@ -13,11 +13,12 @@
* limitations under the License.
*/
-#include <common_time/ICommonTimeConfig.h>
-
#ifndef ANDROID_COMMON_TIME_CONFIG_SERVICE_H
#define ANDROID_COMMON_TIME_CONFIG_SERVICE_H
+#include <sys/socket.h>
+#include <common_time/ICommonTimeConfig.h>
+
namespace android {
class String16;
diff --git a/services/common_time/common_time_server.h b/services/common_time/common_time_server.h
index a0f549f..89bca64 100644
--- a/services/common_time/common_time_server.h
+++ b/services/common_time/common_time_server.h
@@ -19,7 +19,7 @@
#include <arpa/inet.h>
#include <stdint.h>
-#include <linux/socket.h>
+#include <sys/socket.h>
#include <common_time/ICommonClock.h>
#include <common_time/local_clock.h>
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 230f07b..86ada40 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -2490,6 +2490,11 @@
// @see bug/4455071
handleConnectivityChange(info.getType(), false);
break;
+ case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED:
+ info = (NetworkInfo) msg.obj;
+ type = info.getType();
+ updateNetworkSettings(mNetTrackers[type]);
+ break;
case EVENT_CLEAR_NET_TRANSITION_WAKELOCK:
String causedBy = null;
synchronized (ConnectivityService.this) {
diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java
index b609867..72fde11 100755
--- a/services/java/com/android/server/VibratorService.java
+++ b/services/java/com/android/server/VibratorService.java
@@ -441,7 +441,7 @@
private void delay(long duration) {
if (duration > 0) {
- long bedtime = SystemClock.uptimeMillis();
+ long bedtime = duration + SystemClock.uptimeMillis();
do {
try {
this.wait(duration);
@@ -451,8 +451,7 @@
if (mDone) {
break;
}
- duration = duration
- - SystemClock.uptimeMillis() - bedtime;
+ duration = bedtime - SystemClock.uptimeMillis();
} while (duration > 0);
}
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 60085f4..5d74cf3 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -106,6 +106,7 @@
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.SELinux;
import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
@@ -3032,7 +3033,12 @@
File tracesFile = new File(tracesPath);
try {
File tracesDir = tracesFile.getParentFile();
- if (!tracesDir.exists()) tracesFile.mkdirs();
+ if (!tracesDir.exists()) {
+ tracesFile.mkdirs();
+ if (!SELinux.restorecon(tracesDir)) {
+ return null;
+ }
+ }
FileUtils.setPermissions(tracesDir.getPath(), 0775, -1, -1); // drwxrwxr-x
if (clearTraces && tracesFile.exists()) tracesFile.delete();
@@ -3136,7 +3142,12 @@
final File tracesDir = tracesFile.getParentFile();
final File tracesTmp = new File(tracesDir, "__tmp__");
try {
- if (!tracesDir.exists()) tracesFile.mkdirs();
+ if (!tracesDir.exists()) {
+ tracesFile.mkdirs();
+ if (!SELinux.restorecon(tracesDir.getPath())) {
+ return;
+ }
+ }
FileUtils.setPermissions(tracesDir.getPath(), 0775, -1, -1); // drwxrwxr-x
if (tracesFile.exists()) {
@@ -10950,7 +10961,9 @@
restart = true;
} else {
removeDyingProviderLocked(app, cpr, true);
+ // cpr should have been removed from mLaunchingProviders
NL = mLaunchingProviders.size();
+ i--;
}
}
}
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 9171e47..b9e63b7 100755
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -2762,7 +2762,8 @@
// If the top activity in the task is the root
// activity, deliver this new intent to it if it
// desires.
- if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
+ if (((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
+ || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP)
&& taskTop.realActivity.equals(r.realActivity)) {
logStartActivity(EventLogTags.AM_NEW_INTENT, r, taskTop.task);
if (taskTop.frontOfTask) {
diff --git a/services/java/com/android/server/am/DeviceMonitor.java b/services/java/com/android/server/am/DeviceMonitor.java
index 5f3b0ce..21e7252 100644
--- a/services/java/com/android/server/am/DeviceMonitor.java
+++ b/services/java/com/android/server/am/DeviceMonitor.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import android.os.SELinux;
import android.util.Slog;
import java.io.*;
@@ -80,6 +81,9 @@
if (!BASE.isDirectory() && !BASE.mkdirs()) {
throw new AssertionError("Couldn't create " + BASE + ".");
}
+ if (!SELinux.restorecon(BASE)) {
+ throw new AssertionError("Couldn't restorecon " + BASE + ".");
+ }
}
private static final File[] PATHS = {
diff --git a/services/java/com/android/server/am/ProviderMap.java b/services/java/com/android/server/am/ProviderMap.java
index d148ec3..e4608a2 100644
--- a/services/java/com/android/server/am/ProviderMap.java
+++ b/services/java/com/android/server/am/ProviderMap.java
@@ -127,7 +127,12 @@
Slog.i(TAG,
"Removing from providersByName name=" + name + " user="
+ (optionalUserId == -1 ? Binder.getOrigCallingUser() : optionalUserId));
- getProvidersByName(optionalUserId).remove(name);
+ HashMap<String, ContentProviderRecord> map = getProvidersByName(optionalUserId);
+ // map returned by getProvidersByName wouldn't be null
+ map.remove(name);
+ if (map.size() == 0) {
+ mProvidersByNamePerUser.remove(optionalUserId);
+ }
}
}
@@ -141,7 +146,12 @@
Slog.i(TAG,
"Removing from providersByClass name=" + name + " user="
+ (optionalUserId == -1 ? Binder.getOrigCallingUser() : optionalUserId));
- getProvidersByClass(optionalUserId).remove(name);
+ HashMap<ComponentName, ContentProviderRecord> map = getProvidersByClass(optionalUserId);
+ // map returned by getProvidersByClass wouldn't be null
+ map.remove(name);
+ if (map.size() == 0) {
+ mProvidersByClassPerUser.remove(optionalUserId);
+ }
}
}
diff --git a/telephony/java/com/android/internal/telephony/IccCardConstants.java b/telephony/java/com/android/internal/telephony/IccCardConstants.java
index 20439bc..236bb2f 100644
--- a/telephony/java/com/android/internal/telephony/IccCardConstants.java
+++ b/telephony/java/com/android/internal/telephony/IccCardConstants.java
@@ -22,6 +22,8 @@
/* The extra data for broacasting intent INTENT_ICC_STATE_CHANGE */
public static final String INTENT_KEY_ICC_STATE = "ss";
+ /* UNKNOWN means the ICC state is unknown */
+ public static final String INTENT_VALUE_ICC_UNKNOWN = "UNKNOWN";
/* NOT_READY means the ICC interface is not ready (eg, radio is off or powering on) */
public static final String INTENT_VALUE_ICC_NOT_READY = "NOT_READY";
/* ABSENT means ICC is missing */
@@ -45,10 +47,13 @@
/* PERM_DISABLED means ICC is permanently disabled due to puk fails */
public static final String INTENT_VALUE_ABSENT_ON_PERM_DISABLED = "PERM_DISABLED";
- /*
- UNKNOWN is a transient state, for example, after uesr inputs ICC pin under
- PIN_REQUIRED state, the query for ICC status returns UNKNOWN before it
- turns to READY
+ /**
+ * This is combination of IccCardStatus.CardState and IccCardApplicationStatus.AppState
+ * for external apps (like PhoneApp) to use
+ *
+ * UNKNOWN is a transient state, for example, after user inputs ICC pin under
+ * PIN_REQUIRED state, the query for ICC status returns UNKNOWN before it
+ * turns to READY
*/
public enum State {
UNKNOWN,
diff --git a/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/BallsView.java b/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/BallsView.java
index b3b3756..041782d 100644
--- a/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/BallsView.java
+++ b/tests/RenderScriptTests/Balls/src/com/example/android/rs/balls/BallsView.java
@@ -105,7 +105,7 @@
}
void setAccel(float x, float y, float z) {
- if (mRender == null) {
+ if ((mRender == null) || (mRS == null)) {
return;
}
mRender.setAccel(x, -y);
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index 482f43e..d9b0681 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -34,7 +34,6 @@
endif
-LOCAL_C_INCLUDES += external/expat/lib
LOCAL_C_INCLUDES += external/libpng
LOCAL_C_INCLUDES += external/zlib
LOCAL_C_INCLUDES += build/libs/host/include
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index a5aa0b5..fde3bd6 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -61,7 +61,8 @@
mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL),
mVersionCode(NULL), mVersionName(NULL), mCustomPackage(NULL), mExtraPackages(NULL),
mMaxResVersion(NULL), mDebugMode(false), mNonConstantId(false), mProduct(NULL),
- mUseCrunchCache(false), mArgc(0), mArgv(NULL)
+ mUseCrunchCache(false), mErrorOnFailedInsert(false), mOutputTextSymbols(NULL),
+ mArgc(0), mArgv(NULL)
{}
~Bundle(void) {}
@@ -110,6 +111,8 @@
void setAutoAddOverlay(bool val) { mAutoAddOverlay = val; }
bool getGenDependencies() { return mGenDependencies; }
void setGenDependencies(bool val) { mGenDependencies = val; }
+ bool getErrorOnFailedInsert() { return mErrorOnFailedInsert; }
+ void setErrorOnFailedInsert(bool val) { mErrorOnFailedInsert = val; }
bool getUTF16StringsOption() {
return mWantUTF16 || !isMinSdkAtLeast(SDK_FROYO);
@@ -171,6 +174,8 @@
void setProduct(const char * val) { mProduct = val; }
void setUseCrunchCache(bool val) { mUseCrunchCache = val; }
bool getUseCrunchCache() const { return mUseCrunchCache; }
+ const char* getOutputTextSymbols() const { return mOutputTextSymbols; }
+ void setOutputTextSymbols(const char* val) { mOutputTextSymbols = val; }
/*
* Set and get the file specification.
@@ -276,6 +281,8 @@
bool mNonConstantId;
const char* mProduct;
bool mUseCrunchCache;
+ bool mErrorOnFailedInsert;
+ const char* mOutputTextSymbols;
/* file specification */
int mArgc;
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 9570c66..f398de0 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -69,7 +69,8 @@
" [-F apk-file] [-J R-file-dir] \\\n"
" [--product product1,product2,...] \\\n"
" [-c CONFIGS] [--preferred-configurations CONFIGS] \\\n"
- " [raw-files-dir [raw-files-dir] ...]\n"
+ " [raw-files-dir [raw-files-dir] ...] \\\n"
+ " [--output-text-symbols DIR]\n"
"\n"
" Package the android resources. It will read assets and resources that are\n"
" supplied with the -M -A -S or raw-files-dir arguments. The -J -P -F and -R\n"
@@ -177,6 +178,14 @@
" Make the resources ID non constant. This is required to make an R java class\n"
" that does not contain the final value but is used to make reusable compiled\n"
" libraries that need to access resources.\n"
+ " --error-on-failed-insert\n"
+ " Forces aapt to return an error if it fails to insert values into the manifest\n"
+ " with --debug-mode, --min-sdk-version, --target-sdk-version --version-code\n"
+ " and --version-name.\n"
+ " Insertion typically fails if the manifest already defines the attribute.\n"
+ " --output-text-symbols\n"
+ " Generates a text file containing the resource symbols of the R class in the\n"
+ " specified folder.\n"
" --ignore-assets\n"
" Assets to be ignored. Default pattern is:\n"
" %s\n",
@@ -542,6 +551,17 @@
bundle.setInstrumentationPackageNameOverride(argv[0]);
} else if (strcmp(cp, "-auto-add-overlay") == 0) {
bundle.setAutoAddOverlay(true);
+ } else if (strcmp(cp, "-error-on-failed-insert") == 0) {
+ bundle.setErrorOnFailedInsert(true);
+ } else if (strcmp(cp, "-output-text-symbols") == 0) {
+ argc--;
+ argv++;
+ if (!argc) {
+ fprintf(stderr, "ERROR: No argument supplied for '-output-text-symbols' option\n");
+ wantUsage = true;
+ goto bail;
+ }
+ bundle.setOutputTextSymbols(argv[0]);
} else if (strcmp(cp, "-product") == 0) {
argc--;
argv++;
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index a69adc1..5033418 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -673,24 +673,40 @@
return true;
}
-void addTagAttribute(const sp<XMLNode>& node, const char* ns8,
- const char* attr8, const char* value)
+/*
+ * Inserts an attribute in a given node, only if the attribute does not
+ * exist.
+ * If errorOnFailedInsert is true, and the attribute already exists, returns false.
+ * Returns true otherwise, even if the attribute already exists.
+ */
+bool addTagAttribute(const sp<XMLNode>& node, const char* ns8,
+ const char* attr8, const char* value, bool errorOnFailedInsert)
{
if (value == NULL) {
- return;
+ return true;
}
-
+
const String16 ns(ns8);
const String16 attr(attr8);
-
+
if (node->getAttribute(ns, attr) != NULL) {
+ if (errorOnFailedInsert) {
+ fprintf(stderr, "Error: AndroidManifest.xml already defines %s (in %s);"
+ " cannot insert new value %s.\n",
+ String8(attr).string(), String8(ns).string(), value);
+ return false;
+ }
+
fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s);"
" using existing value in manifest.\n",
String8(attr).string(), String8(ns).string());
- return;
+
+ // don't stop the build.
+ return true;
}
node->addAttribute(ns, attr, String16(value));
+ return true;
}
static void fullyQualifyClassName(const String8& package, sp<XMLNode> node,
@@ -728,11 +744,17 @@
fprintf(stderr, "No <manifest> tag.\n");
return UNKNOWN_ERROR;
}
-
- addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode",
- bundle->getVersionCode());
- addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName",
- bundle->getVersionName());
+
+ bool errorOnFailedInsert = bundle->getErrorOnFailedInsert();
+
+ if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode",
+ bundle->getVersionCode(), errorOnFailedInsert)) {
+ return UNKNOWN_ERROR;
+ }
+ if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName",
+ bundle->getVersionName(), errorOnFailedInsert)) {
+ return UNKNOWN_ERROR;
+ }
if (bundle->getMinSdkVersion() != NULL
|| bundle->getTargetSdkVersion() != NULL
@@ -743,18 +765,27 @@
root->insertChildAt(vers, 0);
}
- addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion",
- bundle->getMinSdkVersion());
- addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "targetSdkVersion",
- bundle->getTargetSdkVersion());
- addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion",
- bundle->getMaxSdkVersion());
+ if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion",
+ bundle->getMinSdkVersion(), errorOnFailedInsert)) {
+ return UNKNOWN_ERROR;
+ }
+ if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "targetSdkVersion",
+ bundle->getTargetSdkVersion(), errorOnFailedInsert)) {
+ return UNKNOWN_ERROR;
+ }
+ if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion",
+ bundle->getMaxSdkVersion(), errorOnFailedInsert)) {
+ return UNKNOWN_ERROR;
+ }
}
if (bundle->getDebugMode()) {
sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
if (application != NULL) {
- addTagAttribute(application, RESOURCES_ANDROID_NAMESPACE, "debuggable", "true");
+ if (!addTagAttribute(application, RESOURCES_ANDROID_NAMESPACE, "debuggable", "true",
+ errorOnFailedInsert)) {
+ return UNKNOWN_ERROR;
+ }
}
}
@@ -1821,6 +1852,110 @@
return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
}
+static status_t writeTextLayoutClasses(
+ FILE* fp, const sp<AaptAssets>& assets,
+ const sp<AaptSymbols>& symbols, bool includePrivate)
+{
+ String16 attr16("attr");
+ String16 package16(assets->getPackage());
+
+ bool hasErrors = false;
+
+ size_t i;
+ size_t N = symbols->getNestedSymbols().size();
+ for (i=0; i<N; i++) {
+ sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
+ String16 nclassName16(symbols->getNestedSymbols().keyAt(i));
+ String8 realClassName(nclassName16);
+ if (fixupSymbol(&nclassName16) != NO_ERROR) {
+ hasErrors = true;
+ }
+ String8 nclassName(nclassName16);
+
+ SortedVector<uint32_t> idents;
+ Vector<uint32_t> origOrder;
+ Vector<bool> publicFlags;
+
+ size_t a;
+ size_t NA = nsymbols->getSymbols().size();
+ for (a=0; a<NA; a++) {
+ const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a));
+ int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32
+ ? sym.int32Val : 0;
+ bool isPublic = true;
+ if (code == 0) {
+ String16 name16(sym.name);
+ uint32_t typeSpecFlags;
+ code = assets->getIncludedResources().identifierForName(
+ name16.string(), name16.size(),
+ attr16.string(), attr16.size(),
+ package16.string(), package16.size(), &typeSpecFlags);
+ if (code == 0) {
+ fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n",
+ nclassName.string(), sym.name.string());
+ hasErrors = true;
+ }
+ isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
+ }
+ idents.add(code);
+ origOrder.add(code);
+ publicFlags.add(isPublic);
+ }
+
+ NA = idents.size();
+
+ fprintf(fp, "int[] styleable %s {", nclassName.string());
+
+ for (a=0; a<NA; a++) {
+ if (a != 0) {
+ fprintf(fp, ",");
+ }
+ fprintf(fp, " 0x%08x", idents[a]);
+ }
+
+ fprintf(fp, " }\n");
+
+ for (a=0; a<NA; a++) {
+ ssize_t pos = idents.indexOf(origOrder.itemAt(a));
+ if (pos >= 0) {
+ const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
+ if (!publicFlags.itemAt(a) && !includePrivate) {
+ continue;
+ }
+ String8 name8(sym.name);
+ String16 comment(sym.comment);
+ String16 typeComment;
+ if (comment.size() <= 0) {
+ comment = getAttributeComment(assets, name8, &typeComment);
+ } else {
+ getAttributeComment(assets, name8, &typeComment);
+ }
+ String16 name(name8);
+ if (fixupSymbol(&name) != NO_ERROR) {
+ hasErrors = true;
+ }
+
+ uint32_t typeSpecFlags = 0;
+ String16 name16(sym.name);
+ assets->getIncludedResources().identifierForName(
+ name16.string(), name16.size(),
+ attr16.string(), attr16.size(),
+ package16.string(), package16.size(), &typeSpecFlags);
+ //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(),
+ // String8(attr16).string(), String8(name16).string(), typeSpecFlags);
+ const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
+
+ fprintf(fp,
+ "int styleable.%s_%s %d\n",
+ nclassName.string(),
+ String8(name).string(), (int)pos);
+ }
+ }
+ }
+
+ return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
+}
+
static status_t writeSymbolClass(
FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
const sp<AaptSymbols>& symbols, const String8& className, int indent,
@@ -1848,7 +1983,6 @@
continue;
}
String16 name(sym.name);
- String8 realName(name);
if (fixupSymbol(&name) != NO_ERROR) {
return UNKNOWN_ERROR;
}
@@ -1960,6 +2094,51 @@
return NO_ERROR;
}
+static status_t writeTextSymbolClass(
+ FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
+ const sp<AaptSymbols>& symbols, const String8& className)
+{
+ size_t i;
+ status_t err = NO_ERROR;
+
+ size_t N = symbols->getSymbols().size();
+ for (i=0; i<N; i++) {
+ const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
+ if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) {
+ continue;
+ }
+
+ if (!assets->isJavaSymbol(sym, includePrivate)) {
+ continue;
+ }
+
+ String16 name(sym.name);
+ if (fixupSymbol(&name) != NO_ERROR) {
+ return UNKNOWN_ERROR;
+ }
+
+ fprintf(fp, "int %s %s 0x%08x\n",
+ className.string(),
+ String8(name).string(), (int)sym.int32Val);
+ }
+
+ N = symbols->getNestedSymbols().size();
+ for (i=0; i<N; i++) {
+ sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
+ String8 nclassName(symbols->getNestedSymbols().keyAt(i));
+ if (nclassName == "styleable") {
+ err = writeTextLayoutClasses(fp, assets, nsymbols, includePrivate);
+ } else {
+ err = writeTextSymbolClass(fp, assets, includePrivate, nsymbols, nclassName);
+ }
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+
+ return NO_ERROR;
+}
+
status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets,
const String8& package, bool includePrivate)
{
@@ -1967,11 +2146,15 @@
return NO_ERROR;
}
+ const char* textSymbolsDest = bundle->getOutputTextSymbols();
+
+ String8 R("R");
const size_t N = assets->getSymbols().size();
for (size_t i=0; i<N; i++) {
sp<AaptSymbols> symbols = assets->getSymbols().valueAt(i);
String8 className(assets->getSymbols().keyAt(i));
String8 dest(bundle->getRClassDir());
+
if (bundle->getMakePackageDirs()) {
String8 pkg(package);
const char* last = pkg.string();
@@ -2003,14 +2186,14 @@
}
fprintf(fp,
- "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n"
- " *\n"
- " * This class was automatically generated by the\n"
- " * aapt tool from the resource data it found. It\n"
- " * should not be modified by hand.\n"
- " */\n"
- "\n"
- "package %s;\n\n", package.string());
+ "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n"
+ " *\n"
+ " * This class was automatically generated by the\n"
+ " * aapt tool from the resource data it found. It\n"
+ " * should not be modified by hand.\n"
+ " */\n"
+ "\n"
+ "package %s;\n\n", package.string());
status_t err = writeSymbolClass(fp, assets, includePrivate, symbols,
className, 0, bundle->getNonConstantId());
@@ -2019,14 +2202,37 @@
}
fclose(fp);
+ if (textSymbolsDest != NULL && R == className) {
+ String8 textDest(textSymbolsDest);
+ textDest.appendPath(className);
+ textDest.append(".txt");
+
+ FILE* fp = fopen(textDest.string(), "w+");
+ if (fp == NULL) {
+ fprintf(stderr, "ERROR: Unable to open text symbol file %s: %s\n",
+ textDest.string(), strerror(errno));
+ return UNKNOWN_ERROR;
+ }
+ if (bundle->getVerbose()) {
+ printf(" Writing text symbols for class %s.\n", className.string());
+ }
+
+ status_t err = writeTextSymbolClass(fp, assets, includePrivate, symbols,
+ className);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ fclose(fp);
+ }
+
// If we were asked to generate a dependency file, we'll go ahead and add this R.java
// as a target in the dependency file right next to it.
- if (bundle->getGenDependencies()) {
+ if (bundle->getGenDependencies() && R == className) {
// Add this R.java to the dependency file
String8 dependencyFile(bundle->getRClassDir());
dependencyFile.appendPath("R.java.d");
- fp = fopen(dependencyFile.string(), "a");
+ FILE *fp = fopen(dependencyFile.string(), "a");
fprintf(fp,"%s \\\n", dest.string());
fclose(fp);
}
@@ -2036,7 +2242,6 @@
}
-
class ProguardKeepSet
{
public:
diff --git a/tools/aapt/StringPool.h b/tools/aapt/StringPool.h
index d501008..16050b2 100644
--- a/tools/aapt/StringPool.h
+++ b/tools/aapt/StringPool.h
@@ -21,7 +21,7 @@
#include <ctype.h>
#include <errno.h>
-#include <expat.h>
+#include <libexpat/expat.h>
using namespace android;
diff --git a/voip/java/android/net/sip/SimpleSessionDescription.java b/voip/java/android/net/sip/SimpleSessionDescription.java
index 29166dc..9fcd21d 100644
--- a/voip/java/android/net/sip/SimpleSessionDescription.java
+++ b/voip/java/android/net/sip/SimpleSessionDescription.java
@@ -18,6 +18,7 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Locale;
/**
* An object used to manipulate messages of Session Description Protocol (SDP).
@@ -66,7 +67,7 @@
public SimpleSessionDescription(long sessionId, String address) {
address = (address.indexOf(':') < 0 ? "IN IP4 " : "IN IP6 ") + address;
mFields.parse("v=0");
- mFields.parse(String.format("o=- %d %d %s", sessionId,
+ mFields.parse(String.format(Locale.US, "o=- %d %d %s", sessionId,
System.currentTimeMillis(), address));
mFields.parse("s=-");
mFields.parse("t=0 0");
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
index a447c86..17c930b 100644
--- a/wifi/java/android/net/wifi/WifiMonitor.java
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -20,6 +20,8 @@
import android.net.wifi.p2p.WifiP2pConfig;
import android.net.wifi.p2p.WifiP2pDevice;
import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.WifiP2pService;
+import android.net.wifi.p2p.WifiP2pService.P2pStatus;
import android.net.wifi.p2p.WifiP2pProvDiscEvent;
import android.net.wifi.p2p.nsd.WifiP2pServiceResponse;
import android.net.wifi.StateChangeResult;
@@ -186,7 +188,7 @@
/* P2P-GROUP-STARTED p2p-wlan0-0 [client|GO] ssid="DIRECT-W8" freq=2437
[psk=2182b2e50e53f260d04f3c7b25ef33c965a3291b9b36b455a82d77fd82ca15bc|passphrase="fKG4jMe3"]
- go_dev_addr=fa:7b:7a:42:02:13 */
+ go_dev_addr=fa:7b:7a:42:02:13 [PERSISTENT] */
private static final String P2P_GROUP_STARTED_STR = "P2P-GROUP-STARTED";
/* P2P-GROUP-REMOVED p2p-wlan0-0 [client|GO] reason=REQUESTED */
@@ -594,7 +596,13 @@
if (tokens.length != 2) return;
String[] nameValue = tokens[1].split("=");
if (nameValue.length != 2) return;
- mStateMachine.sendMessage(P2P_INVITATION_RESULT_EVENT, nameValue[1]);
+ P2pStatus err = P2pStatus.UNKNOWN;
+ try {
+ err = P2pStatus.valueOf(Integer.parseInt(nameValue[1]));
+ } catch (NumberFormatException e) {
+ e.printStackTrace();
+ }
+ mStateMachine.sendMessage(P2P_INVITATION_RESULT_EVENT, err);
} else if (dataString.startsWith(P2P_PROV_DISC_PBC_REQ_STR)) {
mStateMachine.sendMessage(P2P_PROV_DISC_PBC_REQ_EVENT,
new WifiP2pProvDiscEvent(dataString));
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 4bf1ca3..e520185 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -547,10 +547,9 @@
break;
}
- //TODO: Add persist behavior once the supplicant interaction is fixed for both
- // group and client scenarios
- /* Persist unless there is an explicit request to not do so*/
- //if (config.persist != WifiP2pConfig.Persist.NO) args.add("persistent");
+ if (config.netId == WifiP2pGroup.PERSISTENT_NET_ID) {
+ args.add("persistent");
+ }
if (joinExistingGroup) {
args.add("join");
@@ -592,10 +591,17 @@
return false;
}
- public boolean p2pGroupAdd() {
+ public boolean p2pGroupAdd(boolean persistent) {
+ if (persistent) {
+ return doBooleanCommand("P2P_GROUP_ADD persistent");
+ }
return doBooleanCommand("P2P_GROUP_ADD");
}
+ public boolean p2pGroupAdd(int netId) {
+ return doBooleanCommand("P2P_GROUP_ADD persistent=" + netId);
+ }
+
public boolean p2pGroupRemove(String iface) {
if (TextUtils.isEmpty(iface)) return false;
return doBooleanCommand("P2P_GROUP_REMOVE " + iface);
@@ -624,6 +630,9 @@
return doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + deviceAddress);
}
+ public String p2pGetSsid(String deviceAddress) {
+ return p2pGetParam(deviceAddress, "oper_ssid");
+ }
public String p2pGetDeviceAddress() {
String status = status();
@@ -665,6 +674,24 @@
return doStringCommand("P2P_PEER " + deviceAddress);
}
+ private String p2pGetParam(String deviceAddress, String key) {
+ if (deviceAddress == null) return null;
+
+ String peerInfo = p2pPeer(deviceAddress);
+ if (peerInfo == null) return null;
+ String[] tokens= peerInfo.split("\n");
+
+ key += "=";
+ for (String token : tokens) {
+ if (token.startsWith(key)) {
+ String[] nameValue = token.split("=");
+ if (nameValue.length != 2) break;
+ return nameValue[1];
+ }
+ }
+ return null;
+ }
+
public boolean p2pServiceAdd(WifiP2pServiceInfo servInfo) {
/*
* P2P_SERVICE_ADD bonjour <query hexdump> <RDATA hexdump>
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index bb09704..78c4b6c 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -3535,6 +3535,13 @@
if (DBG) log("Network connection lost");
handleNetworkDisconnect();
break;
+ case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
+ // Disregard auth failure events during WPS connection. The
+ // EAP sequence is retried several times, and there might be
+ // failures (especially for wps pin). We will get a WPS_XXX
+ // event at the end of the sequence anyway.
+ if (DBG) log("Ignore auth failure during WPS connection");
+ break;
case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
//Throw away supplicant state changes when WPS is running.
//We will start getting supplicant state changes once we get
diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
index 1a42f93..c6d3eae 100644
--- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
@@ -881,6 +881,9 @@
//test to avoid any wifi connectivity issues
loge("ARP test initiation failure: " + se);
success = true;
+ } catch (IllegalArgumentException ae) {
+ log("ARP test initiation failure: " + ae);
+ success = true;
}
return success;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
index 6aea090..100e062 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
@@ -46,18 +46,8 @@
*/
public int groupOwnerIntent = -1;
- /**
- * Indicates whether the configuration is saved
- * @hide
- */
- public enum Persist {
- SYSTEM_DEFAULT,
- YES,
- NO
- }
-
/** @hide */
- public Persist persist = Persist.SYSTEM_DEFAULT;
+ public int netId = WifiP2pGroup.PERSISTENT_NET_ID;
public WifiP2pConfig() {
//set defaults
@@ -110,7 +100,7 @@
sbuf.append("\n address: ").append(deviceAddress);
sbuf.append("\n wps: ").append(wps);
sbuf.append("\n groupOwnerIntent: ").append(groupOwnerIntent);
- sbuf.append("\n persist: ").append(persist.toString());
+ sbuf.append("\n persist: ").append(netId);
return sbuf.toString();
}
@@ -125,7 +115,7 @@
deviceAddress = source.deviceAddress;
wps = new WpsInfo(source.wps);
groupOwnerIntent = source.groupOwnerIntent;
- persist = source.persist;
+ netId = source.netId;
}
}
@@ -134,7 +124,7 @@
dest.writeString(deviceAddress);
dest.writeParcelable(wps, flags);
dest.writeInt(groupOwnerIntent);
- dest.writeString(persist.name());
+ dest.writeInt(netId);
}
/** Implement the Parcelable interface */
@@ -145,7 +135,7 @@
config.deviceAddress = in.readString();
config.wps = (WpsInfo) in.readParcelable(null);
config.groupOwnerIntent = in.readInt();
- config.persist = Persist.valueOf(in.readString());
+ config.netId = in.readInt();
return config;
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
index afdc9be..c86ec8b 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
@@ -231,11 +231,26 @@
return (deviceCapability & DEVICE_CAPAB_SERVICE_DISCOVERY) != 0;
}
+ /** Returns true if the device is capable of invitation {@hide}*/
+ public boolean isInvitationCapable() {
+ return (deviceCapability & DEVICE_CAPAB_INVITATION_PROCEDURE) != 0;
+ }
+
+ /** Returns true if the device reaches the limit. {@hide}*/
+ public boolean isDeviceLimit() {
+ return (deviceCapability & DEVICE_CAPAB_DEVICE_LIMIT) != 0;
+ }
+
/** Returns true if the device is a group owner */
public boolean isGroupOwner() {
return (groupCapability & GROUP_CAPAB_GROUP_OWNER) != 0;
}
+ /** Returns true if the group reaches the limit. {@hide}*/
+ public boolean isGroupLimit() {
+ return (groupCapability & GROUP_CAPAB_GROUP_LIMIT) != 0;
+ }
+
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
index c30cc73..bc492b3 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
@@ -33,6 +33,16 @@
*/
public class WifiP2pGroup implements Parcelable {
+ /** The temporary network id.
+ * {@hide} */
+ public static final int TEMPORARY_NET_ID = -1;
+
+ /** The persistent network id.
+ * If a matching persistent profile is found, use it.
+ * Otherwise, create a new persistent profile.
+ * {@hide} */
+ public static final int PERSISTENT_NET_ID = -2;
+
/** The network name */
private String mNetworkName;
@@ -50,13 +60,17 @@
private String mInterface;
+ /** The network id in the wpa_supplicant */
+ private int mNetId;
+
/** P2P group started string pattern */
private static final Pattern groupStartedPattern = Pattern.compile(
"ssid=\"(.+)\" " +
"freq=(\\d+) " +
"(?:psk=)?([0-9a-fA-F]{64})?" +
"(?:passphrase=)?(?:\"(.{8,63})\")? " +
- "go_dev_addr=((?:[0-9a-f]{2}:){5}[0-9a-f]{2})"
+ "go_dev_addr=((?:[0-9a-f]{2}:){5}[0-9a-f]{2})" +
+ " ?(\\[PERSISTENT\\])?"
);
public WifiP2pGroup() {
@@ -67,13 +81,15 @@
*
* P2P-GROUP-STARTED p2p-wlan0-0 [client|GO] ssid="DIRECT-W8" freq=2437
* [psk=2182b2e50e53f260d04f3c7b25ef33c965a3291b9b36b455a82d77fd82ca15bc|
- * passphrase="fKG4jMe3"] go_dev_addr=fa:7b:7a:42:02:13
+ * passphrase="fKG4jMe3"] go_dev_addr=fa:7b:7a:42:02:13 [PERSISTENT]
*
* P2P-GROUP-REMOVED p2p-wlan0-0 [client|GO] reason=REQUESTED
*
* P2P-INVITATION-RECEIVED sa=fa:7b:7a:42:02:13 go_dev_addr=f8:7b:7a:42:02:13
* bssid=fa:7b:7a:42:82:13 unknown-network
*
+ * P2P-INVITATION-RECEIVED sa=b8:f9:34:2a:c7:9d persistent=0
+ *
* Note: The events formats can be looked up in the wpa_supplicant code
* @hide
*/
@@ -100,16 +116,38 @@
//String psk = match.group(3);
mPassphrase = match.group(4);
mOwner = new WifiP2pDevice(match.group(5));
-
+ if (match.group(6) != null) {
+ mNetId = PERSISTENT_NET_ID;
+ } else {
+ mNetId = TEMPORARY_NET_ID;
+ }
} else if (tokens[0].equals("P2P-INVITATION-RECEIVED")) {
+ String sa = null;
+ mNetId = PERSISTENT_NET_ID;
for (String token : tokens) {
String[] nameValue = token.split("=");
if (nameValue.length != 2) continue;
+ if (nameValue[0].equals("sa")) {
+ sa = nameValue[1];
+
+ // set source address into the client list.
+ WifiP2pDevice dev = new WifiP2pDevice();
+ dev.deviceAddress = nameValue[1];
+ mClients.add(dev);
+ continue;
+ }
+
if (nameValue[0].equals("go_dev_addr")) {
mOwner = new WifiP2pDevice(nameValue[1]);
continue;
}
+
+ if (nameValue[0].equals("persistent")) {
+ mOwner = new WifiP2pDevice(sa);
+ mNetId = Integer.parseInt(nameValue[1]);
+ continue;
+ }
}
} else {
throw new IllegalArgumentException("Malformed supplicant event");
@@ -212,6 +250,16 @@
return mInterface;
}
+ /** @hide */
+ public int getNetworkId() {
+ return mNetId;
+ }
+
+ /** @hide */
+ public void setNetworkId(int netId) {
+ this.mNetId = netId;
+ }
+
public String toString() {
StringBuffer sbuf = new StringBuffer();
sbuf.append("network: ").append(mNetworkName);
@@ -221,6 +269,7 @@
sbuf.append("\n Client: ").append(client);
}
sbuf.append("\n interface: ").append(mInterface);
+ sbuf.append("\n networkId: ").append(mNetId);
return sbuf.toString();
}
@@ -238,6 +287,7 @@
for (WifiP2pDevice d : source.getClientList()) mClients.add(d);
mPassphrase = source.getPassphrase();
mInterface = source.getInterface();
+ mNetId = source.getNetworkId();
}
}
@@ -252,6 +302,7 @@
}
dest.writeString(mPassphrase);
dest.writeString(mInterface);
+ dest.writeInt(mNetId);
}
/** Implement the Parcelable interface */
@@ -268,6 +319,7 @@
}
group.setPassphrase(in.readString());
group.setInterface(in.readString());
+ group.setNetworkId(in.readInt());
return group;
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.aidl b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.aidl
new file mode 100644
index 0000000..3d8a476
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.p2p;
+
+parcelable WifiP2pGroupList;
\ No newline at end of file
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java
new file mode 100644
index 0000000..3459a5a
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.wifi.p2p;
+
+import java.util.Collection;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.LruCache;
+
+
+/**
+ * A class representing a Wi-Fi P2p group list
+ *
+ * {@see WifiP2pManager}
+ * @hide
+ */
+public class WifiP2pGroupList implements Parcelable {
+
+ private static final int CREDENTIAL_MAX_NUM = 32;
+
+ private LruCache<Integer, WifiP2pGroup> mGroups;
+ private GroupDeleteListener mListener;
+ private boolean isClearCalled = false;
+
+ public interface GroupDeleteListener {
+ public void onDeleteGroup(int netId);
+ }
+
+ WifiP2pGroupList() {
+ this(null);
+ }
+
+ WifiP2pGroupList(GroupDeleteListener listener) {
+ mListener = listener;
+ mGroups = new LruCache<Integer, WifiP2pGroup>(CREDENTIAL_MAX_NUM) {
+ @Override
+ protected void entryRemoved(boolean evicted, Integer netId,
+ WifiP2pGroup oldValue, WifiP2pGroup newValue) {
+ if (mListener != null && !isClearCalled) {
+ mListener.onDeleteGroup(oldValue.getNetworkId());
+ }
+ }
+ };
+ }
+
+ /**
+ * Return the list of p2p group.
+ *
+ * @return the list of p2p group.
+ */
+ public Collection<WifiP2pGroup> getGroupList() {
+ return mGroups.snapshot().values();
+ }
+
+ /**
+ * Add the specified group to this group list.
+ *
+ * @param group
+ */
+ void add(WifiP2pGroup group) {
+ mGroups.put(group.getNetworkId(), group);
+ }
+
+ /**
+ * Remove the group with the specified network id from this group list.
+ *
+ * @param netId
+ */
+ void remove(int netId) {
+ mGroups.remove(netId);
+ }
+
+ /**
+ * Remove the group with the specified device address from this group list.
+ *
+ * @param deviceAddress
+ */
+ void remove(String deviceAddress) {
+ remove(getNetworkId(deviceAddress));
+ }
+
+ /**
+ * Clear the group.
+ */
+ boolean clear() {
+ if (mGroups.size() == 0) return false;
+ isClearCalled = true;
+ mGroups.evictAll();
+ isClearCalled = false;
+ return true;
+ }
+
+ /**
+ * Return the network id of the group owner profile with the specified p2p device
+ * address.
+ * If more than one persistent group of the same address is present in the list,
+ * return the first one.
+ *
+ * @param deviceAddress p2p device address.
+ * @return the network id. if not found, return -1.
+ */
+ int getNetworkId(String deviceAddress) {
+ if (deviceAddress == null) return -1;
+
+ final Collection<WifiP2pGroup> groups = mGroups.snapshot().values();
+ for (WifiP2pGroup grp: groups) {
+ if (deviceAddress.equalsIgnoreCase(grp.getOwner().deviceAddress)) {
+ // update cache ordered.
+ mGroups.get(grp.getNetworkId());
+ return grp.getNetworkId();
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Return the network id of the group with the specified p2p device address
+ * and the ssid.
+ *
+ * @param deviceAddress p2p device address.
+ * @param ssid ssid.
+ * @return the network id. if not found, return -1.
+ */
+ int getNetworkId(String deviceAddress, String ssid) {
+ if (deviceAddress == null || ssid == null) {
+ return -1;
+ }
+
+ final Collection<WifiP2pGroup> groups = mGroups.snapshot().values();
+ for (WifiP2pGroup grp: groups) {
+ if (deviceAddress.equalsIgnoreCase(grp.getOwner().deviceAddress) &&
+ ssid.equals(grp.getNetworkName())) {
+ // update cache ordered.
+ mGroups.get(grp.getNetworkId());
+ return grp.getNetworkId();
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Return the group owner address of the group with the specified network id
+ *
+ * @param netId network id.
+ * @return the address. if not found, return null.
+ */
+ String getOwnerAddr(int netId) {
+ WifiP2pGroup grp = mGroups.get(netId);
+ if (grp != null) {
+ return grp.getOwner().deviceAddress;
+ }
+ return null;
+ }
+
+ /**
+ * Return true if this group list contains the specified network id.
+ * This function does NOT update LRU information.
+ * It means the internal queue is NOT reordered.
+ *
+ * @param netId network id.
+ * @return true if the specified network id is present in this group list.
+ */
+ boolean contains(int netId) {
+ final Collection<WifiP2pGroup> groups = mGroups.snapshot().values();
+ for (WifiP2pGroup grp: groups) {
+ if (netId == grp.getNetworkId()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public String toString() {
+ StringBuffer sbuf = new StringBuffer();
+
+ final Collection<WifiP2pGroup> groups = mGroups.snapshot().values();
+ for (WifiP2pGroup grp: groups) {
+ sbuf.append(grp).append("\n");
+ }
+ return sbuf.toString();
+ }
+
+ /** Implement the Parcelable interface */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ public void writeToParcel(Parcel dest, int flags) {
+ final Collection<WifiP2pGroup> groups = mGroups.snapshot().values();
+ dest.writeInt(groups.size());
+ for(WifiP2pGroup group : groups) {
+ dest.writeParcelable(group, flags);
+ }
+ }
+
+ /** Implement the Parcelable interface */
+ public static final Creator<WifiP2pGroupList> CREATOR =
+ new Creator<WifiP2pGroupList>() {
+ public WifiP2pGroupList createFromParcel(Parcel in) {
+ WifiP2pGroupList grpList = new WifiP2pGroupList();
+
+ int deviceCount = in.readInt();
+ for (int i = 0; i < deviceCount; i++) {
+ grpList.add((WifiP2pGroup)in.readParcelable(null));
+ }
+ return grpList;
+ }
+
+ public WifiP2pGroupList[] newArray(int size) {
+ return new WifiP2pGroupList[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 2c25e9d..96d3a7f 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -34,10 +34,10 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.Messenger;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.WorkSource;
-import android.os.Messenger;
import android.util.Log;
import com.android.internal.util.AsyncChannel;
@@ -267,6 +267,13 @@
public static final String EXTRA_WIFI_P2P_DEVICE = "wifiP2pDevice";
/**
+ * Broadcast intent action indicating that remembered persistent groups have changed.
+ * @hide
+ */
+ public static final String WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION =
+ "android.net.wifi.p2p.PERSISTENT_GROUPS_CHANGED";
+
+ /**
* The lookup key for a {@link #String} object.
* Retrieve with {@link android.os.Bundle#getString(String)}.
* @hide
@@ -436,6 +443,18 @@
/** @hide */
public static final int SHOW_PIN_REQUESTED = BASE + 58;
+ /** @hide */
+ public static final int DELETE_PERSISTENT_GROUP = BASE + 59;
+ /** @hide */
+ public static final int DELETE_PERSISTENT_GROUP_FAILED = BASE + 60;
+ /** @hide */
+ public static final int DELETE_PERSISTENT_GROUP_SUCCEEDED = BASE + 61;
+
+ /** @hide */
+ public static final int REQUEST_PERSISTENT_GROUP_INFO = BASE + 62;
+ /** @hide */
+ public static final int RESPONSE_PERSISTENT_GROUP_INFO = BASE + 63;
+
/**
* Create a new WifiP2pManager instance. Applications use
* {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
@@ -657,6 +676,15 @@
public void onDetached(int reason);
}
+ /** Interface for callback invocation when stored group info list is available {@hide}*/
+ public interface PersistentGroupInfoListener {
+ /**
+ * The requested stored p2p group info list is available
+ * @param groups Wi-Fi p2p group info list
+ */
+ public void onPersistentGroupInfoAvailable(WifiP2pGroupList groups);
+ }
+
/**
* A channel that connects the application to the Wifi p2p framework.
* Most p2p operations require a Channel as an argument. An instance of Channel is obtained
@@ -713,6 +741,7 @@
case WifiP2pManager.REMOVE_SERVICE_REQUEST_FAILED:
case WifiP2pManager.CLEAR_SERVICE_REQUESTS_FAILED:
case WifiP2pManager.SET_DEVICE_NAME_FAILED:
+ case WifiP2pManager.DELETE_PERSISTENT_GROUP_FAILED:
if (listener != null) {
((ActionListener) listener).onFailure(message.arg1);
}
@@ -732,6 +761,7 @@
case WifiP2pManager.REMOVE_SERVICE_REQUEST_SUCCEEDED:
case WifiP2pManager.CLEAR_SERVICE_REQUESTS_SUCCEEDED:
case WifiP2pManager.SET_DEVICE_NAME_SUCCEEDED:
+ case WifiP2pManager.DELETE_PERSISTENT_GROUP_SUCCEEDED:
if (listener != null) {
((ActionListener) listener).onSuccess();
}
@@ -786,6 +816,13 @@
mDialogListener = null;
}
break;
+ case WifiP2pManager.RESPONSE_PERSISTENT_GROUP_INFO:
+ WifiP2pGroupList groups = (WifiP2pGroupList) message.obj;
+ if (listener != null) {
+ ((PersistentGroupInfoListener) listener).
+ onPersistentGroupInfoAvailable(groups);
+ }
+ break;
default:
Log.d(TAG, "Ignored " + message);
break;
@@ -995,7 +1032,8 @@
*/
public void createGroup(Channel c, ActionListener listener) {
checkChannel(c);
- c.mAsyncChannel.sendMessage(CREATE_GROUP, 0, c.putListener(listener));
+ c.mAsyncChannel.sendMessage(CREATE_GROUP, WifiP2pGroup.PERSISTENT_NET_ID,
+ c.putListener(listener));
}
/**
@@ -1297,6 +1335,40 @@
}
/**
+ * Delete a stored persistent group from the system settings.
+ *
+ * <p> The function call immediately returns after sending a persistent group removal request
+ * to the framework. The application is notified of a success or failure to initiate
+ * group removal through listener callbacks {@link ActionListener#onSuccess} or
+ * {@link ActionListener#onFailure}.
+ *
+ * <p>The persistent p2p group list stored in the system can be obtained by
+ * {@link #requestPersistentGroupInfo(Channel, PersistentGroupInfoListener)} and
+ * a network id can be obtained by {@link WifiP2pGroup#getNetworkId()}.
+ *
+ * @param c is the channel created at {@link #initialize}
+ * @param netId he network id of the p2p group.
+ * @param listener for callbacks on success or failure. Can be null.
+ * @hide
+ */
+ public void deletePersistentGroup(Channel c, int netId, ActionListener listener) {
+ checkChannel(c);
+ c.mAsyncChannel.sendMessage(DELETE_PERSISTENT_GROUP, netId, c.putListener(listener));
+ }
+
+ /**
+ * Request a list of all the persistent p2p groups stored in system.
+ *
+ * @param c is the channel created at {@link #initialize}
+ * @param listener for callback when persistent group info list is available. Can be null.
+ * @hide
+ */
+ public void requestPersistentGroupInfo(Channel c, PersistentGroupInfoListener listener) {
+ checkChannel(c);
+ c.mAsyncChannel.sendMessage(REQUEST_PERSISTENT_GROUP_INFO, 0, c.putListener(listener));
+ }
+
+ /**
* Get a reference to WifiP2pService handler. This is used to establish
* an AsyncChannel communication with WifiService
*
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 5759074..0be2b27 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -45,6 +45,7 @@
import android.net.wifi.WifiNative;
import android.net.wifi.WifiStateMachine;
import android.net.wifi.WpsInfo;
+import android.net.wifi.p2p.WifiP2pGroupList.GroupDeleteListener;
import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
import android.net.wifi.p2p.nsd.WifiP2pServiceRequest;
import android.net.wifi.p2p.nsd.WifiP2pServiceResponse;
@@ -118,6 +119,13 @@
private static final Boolean JOIN_GROUP = true;
private static final Boolean FORM_GROUP = false;
+ private static final Boolean TRY_REINVOCATION = true;;
+ private static final Boolean NO_REINVOCATION = false;
+
+ private static final int CONNECT_FAILURE = -1;
+ private static final int CONNECT_SUCCESS = 0;
+ private static final int NEEDS_PROVISION_REQ = 1;
+
/* Two minutes comes from the wpa_supplicant setting */
private static final int GROUP_CREATING_WAIT_TIME_MS = 120 * 1000;
private static int mGroupCreatingTimeoutIndex = 0;
@@ -191,6 +199,84 @@
private static final String[] DHCP_RANGE = {"192.168.49.2", "192.168.49.254"};
private static final String SERVER_ADDRESS = "192.168.49.1";
+ /**
+ * Error code definition.
+ * see the Table.8 in the WiFi Direct specification for the detail.
+ */
+ public static enum P2pStatus {
+ /* Success. */
+ SUCCESS,
+
+ /* The target device is currently unavailable. */
+ INFORMATION_IS_CURRENTLY_UNAVAILABLE,
+
+ /* Protocol error. */
+ INCOMPATIBLE_PARAMETERS,
+
+ /* The target device reached the limit of the number of the connectable device.
+ * For example, device limit or group limit is set. */
+ LIMIT_REACHED,
+
+ /* Protocol error. */
+ INVALID_PARAMETER,
+
+ /* Unable to accommodate request. */
+ UNABLE_TO_ACCOMMODATE_REQUEST,
+
+ /* Previous protocol error, or disruptive behavior. */
+ PREVIOUS_PROTOCOL_ERROR,
+
+ /* There is no common channels the both devices can use. */
+ NO_COMMON_CHANNE,
+
+ /* Unknown p2p group. For example, Device A tries to invoke the previous persistent group,
+ * but device B has removed the specified credential already. */
+ UNKNOWN_P2P_GROUP,
+
+ /* Both p2p devices indicated an intent of 15 in group owner negotiation. */
+ BOTH_GO_INTENT_15,
+
+ /* Incompatible provisioning method. */
+ INCOMPATIBLE_PROVISIONING_METHOD,
+
+ /* Rejected by user */
+ REJECTED_BY_USER,
+
+ /* Unknown error */
+ UNKNOWN;
+
+ public static P2pStatus valueOf(int error) {
+ switch(error) {
+ case 0 :
+ return SUCCESS;
+ case 1:
+ return INFORMATION_IS_CURRENTLY_UNAVAILABLE;
+ case 2:
+ return INCOMPATIBLE_PARAMETERS;
+ case 3:
+ return LIMIT_REACHED;
+ case 4:
+ return INVALID_PARAMETER;
+ case 5:
+ return UNABLE_TO_ACCOMMODATE_REQUEST;
+ case 6:
+ return PREVIOUS_PROTOCOL_ERROR;
+ case 7:
+ return NO_COMMON_CHANNE;
+ case 8:
+ return UNKNOWN_P2P_GROUP;
+ case 9:
+ return BOTH_GO_INTENT_15;
+ case 10:
+ return INCOMPATIBLE_PROVISIONING_METHOD;
+ case 11:
+ return REJECTED_BY_USER;
+ default:
+ return UNKNOWN;
+ }
+ }
+ }
+
public WifiP2pService(Context context) {
mContext = context;
@@ -273,6 +359,16 @@
private WifiMonitor mWifiMonitor = new WifiMonitor(this, mWifiNative);
private WifiP2pDeviceList mPeers = new WifiP2pDeviceList();
+ private WifiP2pGroupList mGroups = new WifiP2pGroupList(
+ new GroupDeleteListener() {
+ @Override
+ public void onDeleteGroup(int netId) {
+ if (DBG) logd("called onDeleteGroup() netId=" + netId);
+ mWifiNative.removeNetwork(netId);
+ mWifiNative.saveConfig();
+ sendP2pPersistentGroupsChangedBroadcast();
+ }
+ });
private WifiP2pInfo mWifiP2pInfo = new WifiP2pInfo();
private WifiP2pGroup mGroup;
@@ -395,6 +491,10 @@
replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_FAILED,
WifiP2pManager.BUSY);
break;
+ case WifiP2pManager.DELETE_PERSISTENT_GROUP:
+ replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP,
+ WifiP2pManager.BUSY);
+ break;
case WifiP2pManager.REQUEST_PEERS:
replyToMessage(message, WifiP2pManager.RESPONSE_PEERS, mPeers);
break;
@@ -404,6 +504,10 @@
case WifiP2pManager.REQUEST_GROUP_INFO:
replyToMessage(message, WifiP2pManager.RESPONSE_GROUP_INFO, mGroup);
break;
+ case WifiP2pManager.REQUEST_PERSISTENT_GROUP_INFO:
+ replyToMessage(message, WifiP2pManager.RESPONSE_PERSISTENT_GROUP_INFO,
+ mGroups);
+ break;
case WifiP2pManager.SET_DIALOG_LISTENER:
String appPkgName = (String)message.getData().getString(
WifiP2pManager.APP_PKG_BUNDLE_KEY);
@@ -520,6 +624,10 @@
replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_FAILED,
WifiP2pManager.P2P_UNSUPPORTED);
break;
+ case WifiP2pManager.DELETE_PERSISTENT_GROUP:
+ replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
default:
return NOT_HANDLED;
}
@@ -626,6 +734,8 @@
break;
case WifiStateMachine.CMD_DISABLE_P2P:
if (mPeers.clear()) sendP2pPeersChangedBroadcast();
+ if (mGroups.clear()) sendP2pPersistentGroupsChangedBroadcast();
+
mWifiNative.closeSupplicantConnection();
transitionTo(mP2pDisablingState);
break;
@@ -734,6 +844,11 @@
sendServiceResponse(resp);
}
break;
+ case WifiP2pManager.DELETE_PERSISTENT_GROUP:
+ if (DBG) logd(getName() + " delete persistent group");
+ mGroups.remove(message.arg1);
+ replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP_SUCCEEDED);
+ break;
default:
return NOT_HANDLED;
}
@@ -768,47 +883,35 @@
/* Update group capability before connect */
int gc = mWifiNative.getGroupCapability(config.deviceAddress);
mPeers.updateGroupCapability(config.deviceAddress, gc);
-
- if (mSavedPeerConfig != null && config.deviceAddress.equals(
- mSavedPeerConfig.deviceAddress)) {
- mSavedPeerConfig = config;
-
- //Stop discovery before issuing connect
- mWifiNative.p2pStopFind();
- if (mPeers.isGroupOwner(mSavedPeerConfig.deviceAddress)) {
- p2pConnectWithPinDisplay(mSavedPeerConfig, JOIN_GROUP);
- } else {
- p2pConnectWithPinDisplay(mSavedPeerConfig, FORM_GROUP);
- }
- transitionTo(mGroupNegotiationState);
- } else {
- mSavedPeerConfig = config;
- int netId = configuredNetworkId(mSavedPeerConfig.deviceAddress);
- if (netId >= 0) {
- //TODO: if failure, remove config and do a regular p2pConnect()
- mWifiNative.p2pReinvoke(netId, mSavedPeerConfig.deviceAddress);
- } else {
- //Stop discovery before issuing connect
- mWifiNative.p2pStopFind();
- //If peer is a GO, we do not need to send provisional discovery,
- //the supplicant takes care of it.
- if (mPeers.isGroupOwner(mSavedPeerConfig.deviceAddress)) {
- if (DBG) logd("Sending join to GO");
- p2pConnectWithPinDisplay(mSavedPeerConfig, JOIN_GROUP);
- transitionTo(mGroupNegotiationState);
- } else {
- if (DBG) logd("Sending prov disc");
- transitionTo(mProvisionDiscoveryState);
- }
- }
+ int connectRet = connect(config, TRY_REINVOCATION);
+ if (connectRet == CONNECT_FAILURE) {
+ replyToMessage(message, WifiP2pManager.CONNECT_FAILED);
+ break;
}
mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
sendP2pPeersChangedBroadcast();
replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
+ if (connectRet == NEEDS_PROVISION_REQ) {
+ if (DBG) logd("Sending prov disc");
+ transitionTo(mProvisionDiscoveryState);
+ break;
+ }
+ transitionTo(mGroupNegotiationState);
+ break;
+ case WifiP2pManager.STOP_DISCOVERY:
+ if (mWifiNative.p2pStopFind()) {
+ // When discovery stops in inactive state, flush to clear
+ // state peer data
+ mWifiNative.p2pFlush();
+ mServiceDiscReqId = null;
+ replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
+ WifiP2pManager.ERROR);
+ }
break;
case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:
mSavedPeerConfig = (WifiP2pConfig) message.obj;
-
mAutonomousGroup = false;
mJoinExistingGroup = false;
if (!sendConnectNoticeToApp(mPeers.get(mSavedPeerConfig.deviceAddress),
@@ -848,13 +951,6 @@
transitionTo(mUserAuthorizingInvitationState);
}
break;
- case WifiMonitor.P2P_FIND_STOPPED_EVENT:
- // When discovery stops in inactive state, flush to clear
- // state peer data
- mWifiNative.p2pFlush();
- mServiceDiscReqId = null;
- sendP2pDiscoveryChangedBroadcast(false);
- break;
case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
@@ -865,16 +961,44 @@
break;
case WifiP2pManager.CREATE_GROUP:
mAutonomousGroup = true;
- if (mWifiNative.p2pGroupAdd()) {
- replyToMessage(message, WifiP2pManager.CREATE_GROUP_SUCCEEDED);
+ int netId = message.arg1;
+ boolean ret = false;
+ if (netId == WifiP2pGroup.PERSISTENT_NET_ID) {
+ // check if the go persistent group is present.
+ netId = mGroups.getNetworkId(mThisDevice.deviceAddress);
+ if (netId != -1) {
+ ret = mWifiNative.p2pGroupAdd(netId);
+ } else {
+ ret = mWifiNative.p2pGroupAdd(true);
+ }
+ } else {
+ ret = mWifiNative.p2pGroupAdd(false);
+ }
+
+ if (ret) {
+ replyToMessage(message, WifiP2pManager.CREATE_GROUP_SUCCEEDED);
+ transitionTo(mGroupNegotiationState);
+ } else {
+ replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
+ WifiP2pManager.ERROR);
+ // remain at this state.
+ }
+ break;
+ case WifiMonitor.P2P_GROUP_STARTED_EVENT:
+ mGroup = (WifiP2pGroup) message.obj;
+ if (DBG) logd(getName() + " group started");
+
+ if (mGroup.getNetworkId() == WifiP2pGroup.PERSISTENT_NET_ID) {
+ // This is an invocation case.
+ mAutonomousGroup = false;
+ deferMessage(message);
+ transitionTo(mGroupNegotiationState);
} else {
- replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
- WifiP2pManager.ERROR);
+ return NOT_HANDLED;
}
- transitionTo(mGroupNegotiationState);
break;
default:
- return NOT_HANDLED;
+ return NOT_HANDLED;
}
return HANDLED;
}
@@ -941,10 +1065,7 @@
@Override
public void enter() {
if (DBG) logd(getName());
- if (!sendConnectNoticeToApp(mPeers.get(mSavedPeerConfig.deviceAddress),
- mSavedPeerConfig)) {
- notifyInvitationReceived();
- }
+ notifyInvitationReceived();
}
@Override
@@ -953,11 +1074,10 @@
boolean ret = HANDLED;
switch (message.what) {
case PEER_CONNECTION_USER_ACCEPT:
- //TODO: handle persistence
- if (mJoinExistingGroup) {
- p2pConnectWithPinDisplay(mSavedPeerConfig, JOIN_GROUP);
- } else {
- p2pConnectWithPinDisplay(mSavedPeerConfig, FORM_GROUP);
+ if (connect(mSavedPeerConfig, TRY_REINVOCATION) == CONNECT_FAILURE) {
+ handleGroupCreationFailure();
+ transitionTo(mInactiveState);
+ break;
}
mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
sendP2pPeersChangedBroadcast();
@@ -1000,7 +1120,7 @@
if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {
if (DBG) logd("Found a match " + mSavedPeerConfig);
- mWifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP);
+ p2pConnectWithPinDisplay(mSavedPeerConfig);
transitionTo(mGroupNegotiationState);
}
break;
@@ -1013,11 +1133,14 @@
if (DBG) logd("Found a match " + mSavedPeerConfig);
/* we already have the pin */
if (!TextUtils.isEmpty(mSavedPeerConfig.wps.pin)) {
- mWifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP);
+ p2pConnectWithPinDisplay(mSavedPeerConfig);
transitionTo(mGroupNegotiationState);
} else {
mJoinExistingGroup = false;
- transitionTo(mUserAuthorizingInvitationState);
+ if (!sendConnectNoticeToApp(mPeers.get(mSavedPeerConfig.deviceAddress),
+ mSavedPeerConfig)) {
+ transitionTo(mUserAuthorizingInvitationState);
+ }
}
}
break;
@@ -1029,7 +1152,7 @@
if (mSavedPeerConfig.wps.setup == WpsInfo.DISPLAY) {
if (DBG) logd("Found a match " + mSavedPeerConfig);
mSavedPeerConfig.wps.pin = provDisc.pin;
- mWifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP);
+ p2pConnectWithPinDisplay(mSavedPeerConfig);
if (!sendShowPinReqToFrontApp(provDisc.pin)) {
notifyInvitationSent(provDisc.pin, device.deviceAddress);
}
@@ -1062,6 +1185,17 @@
case WifiMonitor.P2P_GROUP_STARTED_EVENT:
mGroup = (WifiP2pGroup) message.obj;
if (DBG) logd(getName() + " group started");
+
+ if (mGroup.getNetworkId() == WifiP2pGroup.PERSISTENT_NET_ID) {
+ /*
+ * update cache information and set network id to mGroup.
+ */
+ updatePersistentNetworks();
+ String devAddr = mGroup.getOwner().deviceAddress;
+ mGroup.setNetworkId(mGroups.getNetworkId(devAddr,
+ mGroup.getNetworkName()));
+ }
+
if (mGroup.isGroupOwner()) {
startDhcpServer(mGroup.getInterface());
} else {
@@ -1090,6 +1224,29 @@
// failure causes supplicant issues. Ignore right now.
case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
break;
+ case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
+ P2pStatus status = (P2pStatus)message.obj;
+ if (status == P2pStatus.SUCCESS) {
+ // invocation was succeeded.
+ // wait P2P_GROUP_STARTED_EVENT.
+ break;
+ } else if (status == P2pStatus.UNKNOWN_P2P_GROUP) {
+ // target device has already removed the credential.
+ // So, remove this credential accordingly.
+ int netId = mSavedPeerConfig.netId;
+ if (netId >= 0) {
+ if (DBG) logd("Remove unknown client from the list");
+ removeClientFromList(netId, mSavedPeerConfig.deviceAddress, true);
+ }
+ }
+
+ // invocation is failed or deferred. Try another way to connect.
+ mSavedPeerConfig.netId = WifiP2pGroup.PERSISTENT_NET_ID;
+ if (connect(mSavedPeerConfig, NO_REINVOCATION) == CONNECT_FAILURE) {
+ handleGroupCreationFailure();
+ transitionTo(mInactiveState);
+ }
+ break;
default:
return NOT_HANDLED;
}
@@ -1152,7 +1309,7 @@
}
}
sendP2pPeersChangedBroadcast();
- if (DBG) loge(getName() + " ap sta disconnected");
+ if (DBG) logd(getName() + " ap sta disconnected");
} else {
loge("Disconnect on unknown device: " + device);
}
@@ -1172,7 +1329,7 @@
}
break;
case WifiP2pManager.REMOVE_GROUP:
- if (DBG) loge(getName() + " remove group");
+ if (DBG) logd(getName() + " remove group");
if (mWifiNative.p2pGroupRemove(mGroup.getInterface())) {
replyToMessage(message, WifiP2pManager.REMOVE_GROUP_SUCCEEDED);
} else {
@@ -1181,7 +1338,7 @@
}
break;
case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
- if (DBG) loge(getName() + " group removed");
+ if (DBG) logd(getName() + " group removed");
Collection <WifiP2pDevice> devices = mGroup.getClientList();
boolean changed = false;
for (WifiP2pDevice d : mPeers.getDeviceList()) {
@@ -1252,6 +1409,7 @@
replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
} else {
logd("Inviting device : " + config.deviceAddress);
+ mSavedPeerConfig = config;
if (mWifiNative.p2pInvite(mGroup, config.deviceAddress)) {
mPeers.updateStatus(config.deviceAddress, WifiP2pDevice.INVITED);
sendP2pPeersChangedBroadcast();
@@ -1263,6 +1421,29 @@
}
// TODO: figure out updating the status to declined when invitation is rejected
break;
+ case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
+ P2pStatus status = (P2pStatus)message.obj;
+ logd("===> INVITATION RESULT EVENT : " + status);
+ if (status == P2pStatus.SUCCESS) {
+ // invocation was succeeded.
+ break;
+ } else if (status == P2pStatus.UNKNOWN_P2P_GROUP) {
+ // target device has already removed the credential.
+ // So, remove this credential accordingly.
+ int netId = mGroup.getNetworkId();
+ if (netId >= 0) {
+ if (DBG) logd("Remove unknown client from the list");
+ if (!removeClientFromList(netId,
+ mSavedPeerConfig.deviceAddress, false)) {
+ // not found the client on the list
+ Slog.e(TAG, "Already removed the client, ignore");
+ break;
+ }
+ // try invitation.
+ sendMessage(WifiP2pManager.CONNECT, mSavedPeerConfig);
+ }
+ }
+ break;
case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
@@ -1304,7 +1485,6 @@
@Override
public void enter() {
if (DBG) logd(getName());
-
notifyInvitationReceived();
}
@@ -1394,6 +1574,13 @@
mContext.sendStickyBroadcast(intent);
}
+ private void sendP2pPersistentGroupsChangedBroadcast() {
+ if (DBG) logd("sending p2p persistent groups changed broadcast");
+ Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mContext.sendStickyBroadcast(intent);
+ }
+
private void startDhcpServer(String intf) {
InterfaceConfiguration ifcg = null;
try {
@@ -1520,11 +1707,246 @@
dialog.show();
}
- //TODO: implement when wpa_supplicant is fixed
- private int configuredNetworkId(String deviceAddress) {
+ /**
+ * Synchronize the persistent group list between
+ * wpa_supplicant and mGroups.
+ */
+ private void updatePersistentNetworks() {
+ String listStr = mWifiNative.listNetworks();
+
+ boolean isSaveRequired = false;
+ String[] lines = listStr.split("\n");
+ // Skip the first line, which is a header
+ for (int i = 1; i < lines.length; i++) {
+ String[] result = lines[i].split("\t");
+ if (result == null || result.length < 4) {
+ continue;
+ }
+ // network-id | ssid | bssid | flags
+ int netId = -1;
+ String ssid = result[1];
+ String bssid = result[2];
+ String flags = result[3];
+ try {
+ netId = Integer.parseInt(result[0]);
+ } catch(NumberFormatException e) {
+ e.printStackTrace();
+ continue;
+ }
+
+ if (flags.indexOf("[CURRENT]") != -1) {
+ continue;
+ }
+ if (flags.indexOf("[P2P-PERSISTENT]") == -1) {
+ /*
+ * The unused profile is sometimes remained when the p2p group formation is failed.
+ * So, we clean up the p2p group here.
+ */
+ if (DBG) logd("clean up the unused persistent group. netId=" + netId);
+ mWifiNative.removeNetwork(netId);
+ isSaveRequired = true;
+ continue;
+ }
+
+ if (mGroups.contains(netId)) {
+ continue;
+ }
+
+ WifiP2pGroup group = new WifiP2pGroup();
+ group.setNetworkId(netId);
+ group.setNetworkName(ssid);
+ String mode = mWifiNative.getNetworkVariable(netId, "mode");
+ if (mode != null && mode.equals("3")) {
+ group.setIsGroupOwner(true);
+ }
+ if (bssid.equalsIgnoreCase(mThisDevice.deviceAddress)) {
+ group.setOwner(mThisDevice);
+ } else {
+ WifiP2pDevice device = new WifiP2pDevice();
+ device.deviceAddress = bssid;
+ group.setOwner(device);
+ }
+ mGroups.add(group);
+ isSaveRequired = true;
+ }
+
+ if (isSaveRequired) {
+ sendP2pPersistentGroupsChangedBroadcast();
+ mWifiNative.saveConfig();
+ }
+ }
+
+ /**
+ * Try to connect to the target device.
+ *
+ * Use the persistent credential if it has been stored.
+ *
+ * @param config
+ * @param tryInvocation if true, try to invoke. Otherwise, never try to invoke.
+ * @return
+ */
+ private int connect(WifiP2pConfig config, boolean tryInvocation) {
+
+ if (config == null) {
+ loge("invalid argument.");
+ return CONNECT_FAILURE;
+ }
+
+ boolean isResp = (mSavedPeerConfig != null &&
+ config.deviceAddress.equals(mSavedPeerConfig.deviceAddress));
+ mSavedPeerConfig = config;
+
+ WifiP2pDevice dev = mPeers.get(config.deviceAddress);
+ if (dev == null) {
+ loge("target device is not found.");
+ return CONNECT_FAILURE;
+ }
+
+ boolean join = dev.isGroupOwner();
+ String ssid = mWifiNative.p2pGetSsid(dev.deviceAddress);
+ if (DBG) logd("target ssid is " + ssid + " join:" + join);
+
+ if (join && dev.isGroupLimit()) {
+ if (DBG) logd("target device reaches group limit.");
+
+ // if the target group has reached the limit,
+ // try group formation.
+ join = false;
+ } else if (join) {
+ int netId = mGroups.getNetworkId(dev.deviceAddress, ssid);
+ if (netId >= 0) {
+ // Skip WPS and start 4way handshake immediately.
+ if (!mWifiNative.p2pGroupAdd(netId)) {
+ return CONNECT_FAILURE;
+ }
+ return CONNECT_SUCCESS;
+ }
+ }
+
+ if (!join && dev.isDeviceLimit()) {
+ loge("target device reaches the device limit.");
+ return CONNECT_FAILURE;
+ }
+
+ if (!join && tryInvocation && dev.isInvitationCapable()) {
+ int netId = WifiP2pGroup.PERSISTENT_NET_ID;
+ if (config.netId >= 0) {
+ if (config.deviceAddress.equals(mGroups.getOwnerAddr(config.netId))) {
+ netId = config.netId;
+ }
+ } else {
+ netId = mGroups.getNetworkId(dev.deviceAddress);
+ }
+ if (netId < 0) {
+ netId = getNetworkIdFromClientList(dev.deviceAddress);
+ }
+ if (DBG) logd("netId related with " + dev.deviceAddress + " = " + netId);
+ if (netId >= 0) {
+
+ // Invoke the persistent group.
+ if (!mWifiNative.p2pReinvoke(netId, dev.deviceAddress)) {
+ loge("p2pReinvoke() failed");
+ return CONNECT_FAILURE;
+ }
+ // Save network id. It'll be used when an invitation result event is received.
+ mSavedPeerConfig.netId = netId;
+ return CONNECT_SUCCESS;
+ }
+ }
+
+ //Stop discovery before issuing connect
+ mWifiNative.p2pStopFind();
+
+ if (!isResp) {
+ return NEEDS_PROVISION_REQ;
+ }
+
+ p2pConnectWithPinDisplay(config);
+ return CONNECT_SUCCESS;
+ }
+
+ /**
+ * Return the network id of the group owner profile which has the p2p client with
+ * the specified device address in it's client list.
+ * If more than one persistent group of the same address is present in its client
+ * lists, return the first one.
+ *
+ * @param deviceAddress p2p device address.
+ * @return the network id. if not found, return -1.
+ */
+ private int getNetworkIdFromClientList(String deviceAddress) {
+ if (deviceAddress == null) return -1;
+
+ Collection<WifiP2pGroup> groups = mGroups.getGroupList();
+ for (WifiP2pGroup group : groups) {
+ int netId = group.getNetworkId();
+ String[] p2pClientList = getClientList(netId);
+ if (p2pClientList == null) continue;
+ for (String client : p2pClientList) {
+ if (deviceAddress.equalsIgnoreCase(client)) {
+ return netId;
+ }
+ }
+ }
return -1;
}
+ /**
+ * Return p2p client list associated with the specified network id.
+ * @param netId network id.
+ * @return p2p client list. if not found, return null.
+ */
+ private String[] getClientList(int netId) {
+ String p2pClients = mWifiNative.getNetworkVariable(netId, "p2p_client_list");
+ if (p2pClients == null) {
+ return null;
+ }
+ return p2pClients.split(" ");
+ }
+
+ /**
+ * Remove the specified p2p client from the specified profile.
+ * @param netId network id of the profile.
+ * @param addr p2p client address to be removed.
+ * @param isRemovable if true, remove the specified profile if its client list becomes empty.
+ * @return whether removing the specified p2p client is successful or not.
+ */
+ private boolean removeClientFromList(int netId, String addr, boolean isRemovable) {
+ StringBuilder modifiedClientList = new StringBuilder();
+ String[] currentClientList = getClientList(netId);
+ boolean isClientRemoved = false;
+ if (currentClientList != null) {
+ for (String client : currentClientList) {
+ if (!client.equalsIgnoreCase(addr)) {
+ modifiedClientList.append(" ");
+ modifiedClientList.append(client);
+ } else {
+ isClientRemoved = true;
+ }
+ }
+ }
+ if (modifiedClientList.length() == 0 && isRemovable) {
+ // the client list is empty. so remove it.
+ if (DBG) logd("Remove unknown network");
+ mGroups.remove(netId);
+ return true;
+ }
+
+ if (!isClientRemoved) {
+ // specified p2p client is not found. already removed.
+ return false;
+ }
+
+ if (DBG) logd("Modified client list: " + modifiedClientList);
+ if (modifiedClientList.length() == 0) {
+ modifiedClientList.append("\"\"");
+ }
+ mWifiNative.setNetworkVariable(netId,
+ "p2p_client_list", modifiedClientList.toString());
+ mWifiNative.saveConfig();
+ return true;
+ }
+
private void setWifiP2pInfoOnGroupFormation(String serverAddress) {
mWifiP2pInfo.groupFormed = true;
mWifiP2pInfo.isGroupOwner = mGroup.isGroupOwner();
@@ -1546,8 +1968,14 @@
return deviceAddress;
}
- private void p2pConnectWithPinDisplay(WifiP2pConfig config, boolean join) {
- String pin = mWifiNative.p2pConnect(config, join);
+ private void p2pConnectWithPinDisplay(WifiP2pConfig config) {
+ WifiP2pDevice dev = mPeers.get(config.deviceAddress);
+ if (dev == null) {
+ loge("target device is not found " + config.deviceAddress);
+ return;
+ }
+
+ String pin = mWifiNative.p2pConnect(config, dev.isGroupOwner());
try {
Integer.parseInt(pin);
if (!sendShowPinReqToFrontApp(pin)) {
@@ -1610,6 +2038,8 @@
mWifiNative.p2pServiceFlush();
mServiceTransactionId = 0;
mServiceDiscReqId = null;
+
+ updatePersistentNetworks();
}
private void updateThisDevice(int status) {
@@ -1738,7 +2168,7 @@
//Application does not have transaction id information
//go through stored requests to remove
boolean removed = false;
- for (int i=0; i < clientInfo.mReqList.size(); i++) {
+ for (int i=0; i<clientInfo.mReqList.size(); i++) {
if (req.equals(clientInfo.mReqList.valueAt(i))) {
removed = true;
clientInfo.mReqList.removeAt(i);
@@ -2077,5 +2507,4 @@
mServList = new ArrayList<WifiP2pServiceInfo>();
}
}
-
}