Merge "Treat restored defaultScale as overview."
diff --git a/Android.mk b/Android.mk
index 5f3327c..9fbdde7 100644
--- a/Android.mk
+++ b/Android.mk
@@ -190,7 +190,6 @@
telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
wifi/java/android/net/wifi/IWifiManager.aidl \
telephony/java/com/android/internal/telephony/IExtendedNetworkService.aidl \
- vpn/java/android/net/vpn/IVpnService.aidl \
voip/java/android/net/sip/ISipSession.aidl \
voip/java/android/net/sip/ISipSessionListener.aidl \
voip/java/android/net/sip/ISipService.aidl
@@ -280,7 +279,6 @@
frameworks/base/telephony/java/android/telephony/ServiceState.aidl \
frameworks/base/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \
frameworks/base/telephony/java/com/android/internal/telephony/ITelephony.aidl \
- frameworks/base/vpn/java/android/net/vpn/IVpnService.aidl \
gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/framework.aidl
$(gen): PRIVATE_SRC_FILES := $(aidl_files)
@@ -452,7 +450,9 @@
-samplecode $(sample_dir)/VoicemailProviderDemo \
resources/samples/VoicemailProviderDemo "Voicemail Provider Demo" \
-samplecode $(sample_dir)/XmlAdapters \
- resources/samples/XmlAdapters "XML Adapters"
+ resources/samples/XmlAdapters "XML Adapters" \
+ -samplecode $(sample_dir)/TtsEngine \
+ resources/samples/TtsEngine "Text To Speech Engine"
## SDK version identifiers used in the published docs
# major[.minor] version for current SDK. (full releases only)
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 8d34636..f3eaeeb 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -100,6 +100,7 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libstagefright_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os)
$(call add-clean-step, rm -rf $(OUT_DIR)target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/keystore/java/android/security/IKeyChainAliasResponse.java)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/vpn)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
diff --git a/api/current.txt b/api/current.txt
index aa08bb4..d009a64 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -208,6 +208,7 @@
field public static final int actionModePasteDrawable = 16843539; // 0x1010313
field public static final int actionModeSelectAllDrawable = 16843648; // 0x1010380
field public static final int actionOverflowButtonStyle = 16843510; // 0x10102f6
+ field public static final int actionProviderClass = 16843678; // 0x101039e
field public static final int actionViewClass = 16843516; // 0x10102fc
field public static final int activatedBackgroundIndicator = 16843517; // 0x10102fd
field public static final int activityCloseEnterAnimation = 16842938; // 0x10100ba
@@ -20568,6 +20569,12 @@
method public abstract boolean onPrepareActionMode(android.view.ActionMode, android.view.Menu);
}
+ public abstract class ActionProvider {
+ ctor public ActionProvider(android.content.Context);
+ method public abstract android.view.View onCreateActionView();
+ method public void onPerformDefaultAction(android.view.View);
+ }
+
public abstract interface ContextMenu implements android.view.Menu {
method public abstract void clearHeader();
method public abstract android.view.ContextMenu setHeaderIcon(int);
@@ -21236,6 +21243,7 @@
public abstract interface MenuItem {
method public abstract boolean collapseActionView();
method public abstract boolean expandActionView();
+ method public abstract android.view.ActionProvider getActionProvider();
method public abstract android.view.View getActionView();
method public abstract char getAlphabeticShortcut();
method public abstract int getGroupId();
@@ -21254,6 +21262,7 @@
method public abstract boolean isChecked();
method public abstract boolean isEnabled();
method public abstract boolean isVisible();
+ method public abstract android.view.MenuItem setActionProvider(android.view.ActionProvider);
method public abstract android.view.MenuItem setActionView(android.view.View);
method public abstract android.view.MenuItem setActionView(int);
method public abstract android.view.MenuItem setAlphabeticShortcut(char);
@@ -25739,6 +25748,14 @@
method public abstract void onStopTrackingTouch(android.widget.SeekBar);
}
+ public class ShareActionProvider extends android.view.ActionProvider {
+ ctor public ShareActionProvider(android.content.Context);
+ method public android.view.View onCreateActionView();
+ method public void setShareHistoryFileName(java.lang.String);
+ method public void setShareIntent(android.view.View, android.content.Intent);
+ field public static final java.lang.String DEFAULT_SHARE_HISTORY_FILE_NAME = "share_history.xml";
+ }
+
public class SimpleAdapter extends android.widget.BaseAdapter implements android.widget.Filterable {
ctor public SimpleAdapter(android.content.Context, java.util.List<? extends java.util.Map<java.lang.String, ?>>, int, java.lang.String[], int[]);
method public int getCount();
diff --git a/cmds/ip-up-vpn/Android.mk b/cmds/ip-up-vpn/Android.mk
new file mode 100644
index 0000000..de81889
--- /dev/null
+++ b/cmds/ip-up-vpn/Android.mk
@@ -0,0 +1,26 @@
+#
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := ip-up-vpn.c
+LOCAL_SHARED_LIBRARIES := libcutils
+LOCAL_MODULE := ip-up-vpn
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/ppp
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
diff --git a/cmds/ip-up-vpn/ip-up-vpn.c b/cmds/ip-up-vpn/ip-up-vpn.c
new file mode 100644
index 0000000..bbf6b14e
--- /dev/null
+++ b/cmds/ip-up-vpn/ip-up-vpn.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <cutils/properties.h>
+
+int main(int argc, char **argv)
+{
+ if (argc > 1 && strlen(argv[1]) > 0) {
+ char dns[PROPERTY_VALUE_MAX];
+ char *dns1 = getenv("DNS1");
+ char *dns2 = getenv("DNS2");
+
+ snprintf(dns, sizeof(dns), "%s %s", dns1 ? dns1 : "", dns2 ? dns2 : "");
+ property_set("vpn.dns", dns);
+ property_set("vpn.via", argv[1]);
+ }
+ return 0;
+}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index fba16e1..d6f5643 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -23,6 +23,7 @@
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
/**
@@ -101,7 +102,11 @@
void protectVpn(in ParcelFileDescriptor socket);
- String prepareVpn(String packageName);
+ boolean prepareVpn(String oldPackage, String newPackage);
ParcelFileDescriptor establishVpn(in VpnConfig config);
+
+ void startLegacyVpn(in VpnConfig config, in String[] racoon, in String[] mtpd);
+
+ LegacyVpnInfo getLegacyVpnInfo();
}
diff --git a/core/java/android/net/http/SslCertificate.java b/core/java/android/net/http/SslCertificate.java
index f35002a..fe6d4eb 100644
--- a/core/java/android/net/http/SslCertificate.java
+++ b/core/java/android/net/http/SslCertificate.java
@@ -23,13 +23,20 @@
import android.view.View;
import android.widget.TextView;
+import java.io.ByteArrayInputStream;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Vector;
-import com.android.org.bouncycastle.asn1.DERObjectIdentifier;
import com.android.org.bouncycastle.asn1.x509.X509Name;
/**
@@ -45,22 +52,31 @@
/**
* Name of the entity this certificate is issued to
*/
- private DName mIssuedTo;
+ private final DName mIssuedTo;
/**
* Name of the entity this certificate is issued by
*/
- private DName mIssuedBy;
+ private final DName mIssuedBy;
/**
* Not-before date from the validity period
*/
- private Date mValidNotBefore;
+ private final Date mValidNotBefore;
/**
* Not-after date from the validity period
*/
- private Date mValidNotAfter;
+ private final Date mValidNotAfter;
+
+ /**
+ * The original source certificate, if available.
+ *
+ * TODO If deprecated constructors are removed, this should always
+ * be available, and saveState and restoreState can be simplified
+ * to be unconditional.
+ */
+ private final X509Certificate mX509Certificate;
/**
* Bundle key names
@@ -69,6 +85,7 @@
private static final String ISSUED_BY = "issued-by";
private static final String VALID_NOT_BEFORE = "valid-not-before";
private static final String VALID_NOT_AFTER = "valid-not-after";
+ private static final String X509_CERTIFICATE = "x509-certificate";
/**
* Saves the certificate state to a bundle
@@ -76,18 +93,21 @@
* @return A bundle with the certificate stored in it or null if fails
*/
public static Bundle saveState(SslCertificate certificate) {
- Bundle bundle = null;
-
- if (certificate != null) {
- bundle = new Bundle();
-
- bundle.putString(ISSUED_TO, certificate.getIssuedTo().getDName());
- bundle.putString(ISSUED_BY, certificate.getIssuedBy().getDName());
-
- bundle.putString(VALID_NOT_BEFORE, certificate.getValidNotBefore());
- bundle.putString(VALID_NOT_AFTER, certificate.getValidNotAfter());
+ if (certificate == null) {
+ return null;
}
-
+ Bundle bundle = new Bundle();
+ bundle.putString(ISSUED_TO, certificate.getIssuedTo().getDName());
+ bundle.putString(ISSUED_BY, certificate.getIssuedBy().getDName());
+ bundle.putString(VALID_NOT_BEFORE, certificate.getValidNotBefore());
+ bundle.putString(VALID_NOT_AFTER, certificate.getValidNotAfter());
+ X509Certificate x509Certificate = certificate.mX509Certificate;
+ if (x509Certificate != null) {
+ try {
+ bundle.putByteArray(X509_CERTIFICATE, x509Certificate.getEncoded());
+ } catch (CertificateEncodingException ignored) {
+ }
+ }
return bundle;
}
@@ -97,15 +117,27 @@
* @return The SSL certificate stored in the bundle or null if fails
*/
public static SslCertificate restoreState(Bundle bundle) {
- if (bundle != null) {
- return new SslCertificate(
- bundle.getString(ISSUED_TO),
- bundle.getString(ISSUED_BY),
- bundle.getString(VALID_NOT_BEFORE),
- bundle.getString(VALID_NOT_AFTER));
+ if (bundle == null) {
+ return null;
}
-
- return null;
+ X509Certificate x509Certificate;
+ byte[] bytes = bundle.getByteArray(X509_CERTIFICATE);
+ if (bytes == null) {
+ x509Certificate = null;
+ } else {
+ try {
+ CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(bytes));
+ x509Certificate = (X509Certificate) cert;
+ } catch (CertificateException e) {
+ x509Certificate = null;
+ }
+ }
+ return new SslCertificate(bundle.getString(ISSUED_TO),
+ bundle.getString(ISSUED_BY),
+ parseDate(bundle.getString(VALID_NOT_BEFORE)),
+ parseDate(bundle.getString(VALID_NOT_AFTER)),
+ x509Certificate);
}
/**
@@ -121,7 +153,7 @@
@Deprecated
public SslCertificate(
String issuedTo, String issuedBy, String validNotBefore, String validNotAfter) {
- this(issuedTo, issuedBy, parseDate(validNotBefore), parseDate(validNotAfter));
+ this(issuedTo, issuedBy, parseDate(validNotBefore), parseDate(validNotAfter), null);
}
/**
@@ -135,10 +167,7 @@
@Deprecated
public SslCertificate(
String issuedTo, String issuedBy, Date validNotBefore, Date validNotAfter) {
- mIssuedTo = new DName(issuedTo);
- mIssuedBy = new DName(issuedBy);
- mValidNotBefore = cloneDate(validNotBefore);
- mValidNotAfter = cloneDate(validNotAfter);
+ this(issuedTo, issuedBy, validNotBefore, validNotAfter, null);
}
/**
@@ -149,7 +178,19 @@
this(certificate.getSubjectDN().getName(),
certificate.getIssuerDN().getName(),
certificate.getNotBefore(),
- certificate.getNotAfter());
+ certificate.getNotAfter(),
+ certificate);
+ }
+
+ private SslCertificate(
+ String issuedTo, String issuedBy,
+ Date validNotBefore, Date validNotAfter,
+ X509Certificate x509Certificate) {
+ mIssuedTo = new DName(issuedTo);
+ mIssuedBy = new DName(issuedBy);
+ mValidNotBefore = cloneDate(validNotBefore);
+ mValidNotAfter = cloneDate(validNotAfter);
+ mX509Certificate = x509Certificate;
}
/**
@@ -205,6 +246,54 @@
}
/**
+ * Convenience for UI presentation, not intended as public API.
+ */
+ private static String getSerialNumber(X509Certificate x509Certificate) {
+ if (x509Certificate == null) {
+ return "";
+ }
+ BigInteger serialNumber = x509Certificate.getSerialNumber();
+ if (serialNumber == null) {
+ return "";
+ }
+ return fingerprint(serialNumber.toByteArray());
+ }
+
+ /**
+ * Convenience for UI presentation, not intended as public API.
+ */
+ private static String getDigest(X509Certificate x509Certificate, String algorithm) {
+ if (x509Certificate == null) {
+ return "";
+ }
+ try {
+ byte[] bytes = x509Certificate.getEncoded();
+ MessageDigest md = MessageDigest.getInstance(algorithm);
+ byte[] digest = md.digest(bytes);
+ return fingerprint(digest);
+ } catch (CertificateEncodingException ignored) {
+ return "";
+ } catch (NoSuchAlgorithmException ignored) {
+ return "";
+ }
+ }
+
+ private static final String fingerprint(byte[] bytes) {
+ if (bytes == null) {
+ return "";
+ }
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < bytes.length; i++) {
+ byte b = bytes[i];
+ IntegralToString.appendByteAsHex(sb, b, true);
+ if (i+1 != bytes.length) {
+ sb.append(':');
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
* @return A string representation of this certificate for debugging
*/
public String toString() {
@@ -338,7 +427,6 @@
* Inflates the SSL certificate view (helper method).
* @return The resultant certificate view with issued-to, issued-by,
* issued-on, expires-on, and possibly other fields set.
- * If the input certificate is null, returns null.
*
* @hide Used by Browser and Settings
*/
@@ -352,33 +440,42 @@
SslCertificate.DName issuedTo = getIssuedTo();
if (issuedTo != null) {
((TextView) certificateView.findViewById(com.android.internal.R.id.to_common))
- .setText(issuedTo.getCName());
+ .setText(issuedTo.getCName());
((TextView) certificateView.findViewById(com.android.internal.R.id.to_org))
- .setText(issuedTo.getOName());
+ .setText(issuedTo.getOName());
((TextView) certificateView.findViewById(com.android.internal.R.id.to_org_unit))
- .setText(issuedTo.getUName());
+ .setText(issuedTo.getUName());
}
+ // serial number:
+ ((TextView) certificateView.findViewById(com.android.internal.R.id.serial_number))
+ .setText(getSerialNumber(mX509Certificate));
// issued by:
SslCertificate.DName issuedBy = getIssuedBy();
if (issuedBy != null) {
((TextView) certificateView.findViewById(com.android.internal.R.id.by_common))
- .setText(issuedBy.getCName());
+ .setText(issuedBy.getCName());
((TextView) certificateView.findViewById(com.android.internal.R.id.by_org))
- .setText(issuedBy.getOName());
+ .setText(issuedBy.getOName());
((TextView) certificateView.findViewById(com.android.internal.R.id.by_org_unit))
- .setText(issuedBy.getUName());
+ .setText(issuedBy.getUName());
}
// issued on:
String issuedOn = formatCertificateDate(context, getValidNotBeforeDate());
((TextView) certificateView.findViewById(com.android.internal.R.id.issued_on))
- .setText(issuedOn);
+ .setText(issuedOn);
// expires on:
String expiresOn = formatCertificateDate(context, getValidNotAfterDate());
((TextView) certificateView.findViewById(com.android.internal.R.id.expires_on))
- .setText(expiresOn);
+ .setText(expiresOn);
+
+ // fingerprints:
+ ((TextView) certificateView.findViewById(com.android.internal.R.id.sha256_fingerprint))
+ .setText(getDigest(mX509Certificate, "SHA256"));
+ ((TextView) certificateView.findViewById(com.android.internal.R.id.sha1_fingerprint))
+ .setText(getDigest(mX509Certificate, "SHA1"));
return certificateView;
}
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 3bc1348..f799af3 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -275,6 +275,14 @@
* @hide
*/
public static final String MEDIA_SCANNER_NEW_OBJECT_ID = "media_scanner_new_object_id";
+
+ /**
+ * Non-zero if the media file is drm-protected
+ * <P>Type: INTEGER (boolean)</P>
+ * @hide
+ */
+ public static final String IS_DRM = "is_drm";
+
}
/**
@@ -1124,19 +1132,19 @@
public static final String IS_PODCAST = "is_podcast";
/**
- * Non-zero id the audio file may be a ringtone
+ * Non-zero if the audio file may be a ringtone
* <P>Type: INTEGER (boolean)</P>
*/
public static final String IS_RINGTONE = "is_ringtone";
/**
- * Non-zero id the audio file may be an alarm
+ * Non-zero if the audio file may be an alarm
* <P>Type: INTEGER (boolean)</P>
*/
public static final String IS_ALARM = "is_alarm";
/**
- * Non-zero id the audio file may be a notification sound
+ * Non-zero if the audio file may be a notification sound
* <P>Type: INTEGER (boolean)</P>
*/
public static final String IS_NOTIFICATION = "is_notification";
diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java
index d99c760..7ea0fbd 100644
--- a/core/java/android/provider/VoicemailContract.java
+++ b/core/java/android/provider/VoicemailContract.java
@@ -53,18 +53,6 @@
/** The authority used by the voicemail provider. */
public static final String AUTHORITY = "com.android.voicemail";
/**
- * URI to insert/retrieve all voicemails.
- * @deprecated
- */
- public static final Uri CONTENT_URI =
- Uri.parse("content://" + AUTHORITY + "/voicemail");
- /**
- * URI to insert/retrieve voicemails by a given voicemail source.
- * @deprecated
- */
- public static final Uri CONTENT_URI_SOURCE =
- Uri.parse("content://" + AUTHORITY + "/voicemail/source/");
- /**
* Parameter key used in the URI to specify the voicemail source package name.
* <p> This field must be set in all requests that originate from a voicemail source.
*/
@@ -81,9 +69,10 @@
public static final String EXTRA_SELF_CHANGE = "com.android.voicemail.extra.SELF_CHANGE";
/**
- * The mime type for a collection of voicemails.
- * @deprecated */
- public static final String DIR_TYPE = "vnd.android.cursor.dir/voicemails";
+ * Name of the source package field, which must be same across all voicemail related tables.
+ * @hide
+ */
+ public static final String SOURCE_PACKAGE_FIELD = "source_package";
/** Defines fields exposed through the /voicemail path of this content provider. */
public static final class Voicemails implements BaseColumns {
@@ -91,12 +80,9 @@
private Voicemails() {
}
- /** URI to insert/retrieve voicemails by a given voicemail source. */
+ /** URI to insert/retrieve voicemails. */
public static final Uri CONTENT_URI =
Uri.parse("content://" + AUTHORITY + "/voicemail");
- /** URI to insert/retrieve voicemails by a given voicemail source. */
- public static final Uri CONTENT_URI_SOURCE =
- Uri.parse("content://" + AUTHORITY + "/voicemail/source/");
/** The mime type for a collection of voicemails. */
public static final String DIR_TYPE = "vnd.android.cursor.dir/voicemails";
@@ -138,7 +124,7 @@
* Package name of the source application that inserted the voicemail.
* <P>Type: TEXT</P>
*/
- public static final String SOURCE_PACKAGE = "source_package";
+ public static final String SOURCE_PACKAGE = SOURCE_PACKAGE_FIELD;
/**
* Application-specific data available to the source application that
* inserted the voicemail. This is typically used to store the source
@@ -191,7 +177,7 @@
* The package name of the voicemail source. There can only be a one entry per source.
* <P>Type: TEXT</P>
*/
- public static final String SOURCE_PACKAGE = "source_package";
+ public static final String SOURCE_PACKAGE = SOURCE_PACKAGE_FIELD;
/**
* The URI to call to invoke source specific voicemail settings screen. On a user request
* to setup voicemail an intent with action VIEW with this URI will be fired by the system.
diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java
index a3686b7..c7603ee 100644
--- a/core/java/android/speech/tts/AudioPlaybackHandler.java
+++ b/core/java/android/speech/tts/AudioPlaybackHandler.java
@@ -74,6 +74,12 @@
removeMessages(token);
if (token.getType() == MessageParams.TYPE_SYNTHESIS) {
+ AudioTrack current = ((SynthesisMessageParams) token).getAudioTrack();
+ if (current != null) {
+ // Stop the current audio track if it's still playing.
+ // The audio track is thread safe in this regard.
+ current.stop();
+ }
mQueue.add(new ListEntry(SYNTHESIS_DONE, token, HIGH_PRIORITY));
} else {
final MessageParams current = getCurrentParams();
@@ -393,9 +399,10 @@
try {
if (audioTrack != null) {
- audioTrack.flush();
- audioTrack.stop();
if (DBG) Log.d(TAG, "Releasing audio track [" + audioTrack.hashCode() + "]");
+ // The last call to AudioTrack.write( ) will return only after
+ // all data from the audioTrack has been sent to the mixer, so
+ // it's safe to release at this point.
audioTrack.release();
}
} finally {
diff --git a/core/java/android/view/ActionProvider.java b/core/java/android/view/ActionProvider.java
new file mode 100644
index 0000000..6491da0
--- /dev/null
+++ b/core/java/android/view/ActionProvider.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.content.Context;
+
+/**
+ * This class is a mediator for accomplishing a given task, for example sharing a file.
+ * It is responsible for creating a view that performs an action that accomplishes the task.
+ * This class also implements other functions such a performing a default action.
+ * <p>
+ * An ActionProvider can be optionally specified for a {@link MenuItem} and in such a
+ * case it will be responsible for creating the action view that appears in the
+ * {@link android.app.ActionBar} as a substitute for the menu item when the item is
+ * displayed as an action item. Also the provider is responsible for performing a
+ * default action if a menu item placed on the overflow menu of the ActionBar is
+ * selected and none of the menu item callbacks has handled the selection.
+ * </p>
+ * <p>
+ * There are two ways for using an action provider for creating and handling of action views:
+ * <ul>
+ * <li>
+ * Setting the action provider on a {@link MenuItem} directly by calling
+ * {@link MenuItem#setActionProvider(ActionProvider)}.
+ * </li>
+ * <li>
+ * Declaring the action provider in the menu XML resource. For example:
+ * <pre>
+ * <code>
+ * <item android:id="@+id/my_menu_item"
+ * android:title="Title"
+ * android:icon="@drawable/my_menu_item_icon"
+ * android:showAsAction="ifRoom"
+ * android:actionProviderClass="foo.bar.SomeActionProvider" />
+ * </code>
+ * </pre>
+ * </li>
+ * </ul>
+ * </p>
+ *
+ * @see MenuItem#setActionProvider(ActionProvider)
+ * @see MenuItem#getActionProvider()
+ */
+public abstract class ActionProvider {
+
+ /**
+ * Creates a new instance.
+ *
+ * @param context Context for accessing resources.
+ */
+ public ActionProvider(Context context) {
+ }
+
+ /**
+ * Factory method for creating new action views.
+ *
+ * @return A new action view.
+ */
+ public abstract View onCreateActionView();
+
+ /**
+ * Performs an optional default action.
+ * <p>
+ * For the case of an action provider placed in a menu item not shown as an action this
+ * method is invoked if none of the callbacks for processing menu selection has handled
+ * the event.
+ * </p>
+ * <p>
+ * A menu item selection is processed in the following order:
+ * <ul>
+ * <li>
+ * Receiving a call to {@link MenuItem.OnMenuItemClickListener#onMenuItemClick
+ * MenuItem.OnMenuItemClickListener.onMenuItemClick}.
+ * </li>
+ * <li>
+ * Receiving a call to {@link android.app.Activity#onOptionsItemSelected(MenuItem)
+ * Activity.onOptionsItemSelected(MenuItem)}
+ * </li>
+ * <li>
+ * Receiving a call to {@link android.app.Fragment#onOptionsItemSelected(MenuItem)
+ * Fragment.onOptionsItemSelected(MenuItem)}
+ * </li>
+ * <li>
+ * Launching the {@link android.content.Intent} set via
+ * {@link MenuItem#setIntent(android.content.Intent) MenuItem.setIntent(android.content.Intent)}
+ * </li>
+ * <li>
+ * Invoking this method.
+ * </li>
+ * </ul>
+ * </p>
+ * <p>
+ * The default implementation does not perform any action.
+ * </p>
+ *
+ * @param actionView A view created by {@link #onCreateActionView()}.
+ */
+ public void onPerformDefaultAction(View actionView) {
+ }
+}
diff --git a/core/java/android/view/MenuInflater.java b/core/java/android/view/MenuInflater.java
index 372ac15..a7f0cba 100644
--- a/core/java/android/view/MenuInflater.java
+++ b/core/java/android/view/MenuInflater.java
@@ -26,6 +26,7 @@
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.Xml;
import java.io.IOException;
@@ -42,6 +43,8 @@
* <em>something</em> file.)
*/
public class MenuInflater {
+ private static final String LOG_TAG = "MenuInflater";
+
/** Menu tag name in XML. */
private static final String XML_MENU = "menu";
@@ -53,10 +56,16 @@
private static final int NO_ID = 0;
- private static final Class<?>[] ACTION_VIEW_CONSTRUCTOR_SIGNATURE = new Class[]{Context.class};
+ private static final Class<?>[] ACTION_VIEW_CONSTRUCTOR_SIGNATURE = new Class[] {Context.class};
+
+ private static final Class<?>[] ACTION_PROVIDER_CONSTRUCTOR_SIGNATURE = ACTION_VIEW_CONSTRUCTOR_SIGNATURE;
+
+ private final Object[] mActionViewConstructorArguments;
+
+ private final Object[] mActionProviderConstructorArguments;
private Context mContext;
-
+
/**
* Constructs a menu inflater.
*
@@ -64,6 +73,8 @@
*/
public MenuInflater(Context context) {
mContext = context;
+ mActionViewConstructorArguments = new Object[] {context};
+ mActionProviderConstructorArguments = mActionViewConstructorArguments;
}
/**
@@ -172,14 +183,14 @@
private static class InflatedOnMenuItemClickListener
implements MenuItem.OnMenuItemClickListener {
- private static final Class[] PARAM_TYPES = new Class[] { MenuItem.class };
+ private static final Class<?>[] PARAM_TYPES = new Class[] { MenuItem.class };
private Context mContext;
private Method mMethod;
public InflatedOnMenuItemClickListener(Context context, String methodName) {
mContext = context;
- Class c = context.getClass();
+ Class<?> c = context.getClass();
try {
mMethod = c.getMethod(methodName, PARAM_TYPES);
} catch (Exception e) {
@@ -255,7 +266,8 @@
private int itemActionViewLayout;
private String itemActionViewClassName;
-
+ private String itemActionProviderClassName;
+
private String itemListenerMethodName;
private static final int defaultGroupId = NO_ID;
@@ -333,9 +345,10 @@
itemListenerMethodName = a.getString(com.android.internal.R.styleable.MenuItem_onClick);
itemActionViewLayout = a.getResourceId(com.android.internal.R.styleable.MenuItem_actionLayout, 0);
itemActionViewClassName = a.getString(com.android.internal.R.styleable.MenuItem_actionViewClass);
-
+ itemActionProviderClassName = a.getString(com.android.internal.R.styleable.MenuItem_actionProviderClass);
+
a.recycle();
-
+
itemAdded = false;
}
@@ -377,20 +390,35 @@
}
}
+ boolean actionViewSpecified = false;
if (itemActionViewClassName != null) {
- try {
- final Class<?> clazz = Class.forName(itemActionViewClassName, true,
- mContext.getClassLoader());
- Constructor<?> c = clazz.getConstructor(ACTION_VIEW_CONSTRUCTOR_SIGNATURE);
- item.setActionView((View) c.newInstance(mContext));
- } catch (Exception e) {
- throw new InflateException(e);
+ View actionView = (View) newInstance(itemActionViewClassName,
+ ACTION_VIEW_CONSTRUCTOR_SIGNATURE, mActionViewConstructorArguments);
+ item.setActionView(actionView);
+ actionViewSpecified = true;
+ }
+ if (itemActionViewLayout > 0) {
+ if (!actionViewSpecified) {
+ item.setActionView(itemActionViewLayout);
+ actionViewSpecified = true;
+ } else {
+ Log.w(LOG_TAG, "Ignoring attribute 'itemActionViewLayout'."
+ + " Action view already specified.");
}
- } else if (itemActionViewLayout > 0) {
- item.setActionView(itemActionViewLayout);
+ }
+ if (itemActionProviderClassName != null) {
+ if (!actionViewSpecified) {
+ ActionProvider actionProvider = newInstance(itemActionProviderClassName,
+ ACTION_PROVIDER_CONSTRUCTOR_SIGNATURE,
+ mActionProviderConstructorArguments);
+ item.setActionProvider(actionProvider);
+ } else {
+ Log.w(LOG_TAG, "Ignoring attribute 'itemActionProviderClass'."
+ + " Action view already specified.");
+ }
}
}
-
+
public void addItem() {
itemAdded = true;
setItem(menu.add(groupId, itemId, itemCategoryOrder, itemTitle));
@@ -406,6 +434,18 @@
public boolean hasAddedItem() {
return itemAdded;
}
+
+ @SuppressWarnings("unchecked")
+ private <T> T newInstance(String className, Class<?>[] constructorSignature,
+ Object[] arguments) {
+ try {
+ Class<?> clazz = mContext.getClassLoader().loadClass(className);
+ Constructor<?> constructor = clazz.getConstructor(constructorSignature);
+ return (T) constructor.newInstance(arguments);
+ } catch (Exception e) {
+ Log.w(LOG_TAG, "Cannot instantiate class: " + className, e);
+ }
+ return null;
+ }
}
-
}
diff --git a/core/java/android/view/MenuItem.java b/core/java/android/view/MenuItem.java
index dc68264..ccd8353 100644
--- a/core/java/android/view/MenuItem.java
+++ b/core/java/android/view/MenuItem.java
@@ -88,7 +88,6 @@
* @see MenuItem#expandActionView()
* @see MenuItem#collapseActionView()
* @see MenuItem#setShowAsActionFlags(int)
- * @see MenuItem#
*/
public interface OnActionExpandListener {
/**
@@ -480,6 +479,10 @@
* Set an action view for this menu item. An action view will be displayed in place
* of an automatically generated menu item element in the UI when this item is shown
* as an action within a parent.
+ * <p>
+ * <strong>Note:</strong> Setting an action view overrides the action provider
+ * set via {@link #setActionProvider(ActionProvider)}.
+ * </p>
*
* @param view View to use for presenting this item to the user.
* @return This Item so additional setters can be called.
@@ -492,6 +495,10 @@
* Set an action view for this menu item. An action view will be displayed in place
* of an automatically generated menu item element in the UI when this item is shown
* as an action within a parent.
+ * <p>
+ * <strong>Note:</strong> Setting an action view overrides the action provider
+ * set via {@link #setActionProvider(ActionProvider)}.
+ * </p>
*
* @param resId Layout resource to use for presenting this item to the user.
* @return This Item so additional setters can be called.
@@ -511,6 +518,32 @@
public View getActionView();
/**
+ * Sets the {@link ActionProvider} responsible for creating an action view if
+ * the item is placed on the action bar. The provider also provides a default
+ * action invoked if the item is placed in the overflow menu.
+ * <p>
+ * <strong>Note:</strong> Setting an action provider overrides the action view
+ * set via {@link #setActionView(int)} or {@link #setActionView(View)}.
+ * </p>
+ *
+ * @param actionProvider The action provider.
+ * @return This Item so additional setters can be called.
+ *
+ * @see ActionProvider
+ */
+ public MenuItem setActionProvider(ActionProvider actionProvider);
+
+ /**
+ * Gets the {@link ActionProvider}.
+ *
+ * @return The action provider.
+ *
+ * @see ActionProvider
+ * @see #setActionProvider(ActionProvider)
+ */
+ public ActionProvider getActionProvider();
+
+ /**
* Expand the action view associated with this menu item.
* The menu item must have an action view set, as well as
* the showAsAction flag {@link #SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}.
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index 9d84c3e..4a98336 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -160,10 +160,10 @@
if (mSubtypeNameResId == 0) {
return localeStr;
}
- final String subtypeName = context.getPackageManager().getText(
- packageName, mSubtypeNameResId, appInfo).toString();
+ final CharSequence subtypeName = context.getPackageManager().getText(
+ packageName, mSubtypeNameResId, appInfo);
if (!TextUtils.isEmpty(subtypeName)) {
- return String.format(subtypeName, localeStr);
+ return String.format(subtypeName.toString(), localeStr);
} else {
return localeStr;
}
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 79a5aff..5aa60f4 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -50,8 +50,10 @@
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.Map;
+import java.util.HashSet;
import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
import org.apache.harmony.security.provider.cert.X509CertImpl;
@@ -86,7 +88,8 @@
private boolean mIsMainFrame;
// Attached Javascript interfaces
- private Map<String, Object> mJSInterfaceMap;
+ private Map<String, Object> mJavaScriptObjects;
+ private Set<Object> mRemovedJavaScriptObjects;
// Key store handler when Chromium HTTP stack is used.
private KeyStoreHandler mKeyStoreHandler = null;
@@ -229,10 +232,11 @@
}
sConfigCallback.addHandler(this);
- mJSInterfaceMap = javascriptInterfaces;
- if (mJSInterfaceMap == null) {
- mJSInterfaceMap = new HashMap<String, Object>();
+ mJavaScriptObjects = javascriptInterfaces;
+ if (mJavaScriptObjects == null) {
+ mJavaScriptObjects = new HashMap<String, Object>();
}
+ mRemovedJavaScriptObjects = new HashSet<Object>();
mSettings = settings;
mContext = context;
@@ -241,7 +245,7 @@
mWebViewCore = w;
mSearchBox = new SearchBoxImpl(mWebViewCore, mCallbackProxy);
- mJSInterfaceMap.put(SearchBoxImpl.JS_INTERFACE_NAME, mSearchBox);
+ mJavaScriptObjects.put(SearchBoxImpl.JS_INTERFACE_NAME, mSearchBox);
AssetManager am = context.getAssets();
nativeCreateFrame(w, am, proxy.getBackForwardList());
@@ -598,15 +602,16 @@
* We should re-attach any attached js interfaces.
*/
private void windowObjectCleared(int nativeFramePointer) {
- Iterator<String> iter = mJSInterfaceMap.keySet().iterator();
+ Iterator<String> iter = mJavaScriptObjects.keySet().iterator();
while (iter.hasNext()) {
String interfaceName = iter.next();
- Object object = mJSInterfaceMap.get(interfaceName);
+ Object object = mJavaScriptObjects.get(interfaceName);
if (object != null) {
nativeAddJavascriptInterface(nativeFramePointer,
- mJSInterfaceMap.get(interfaceName), interfaceName);
+ mJavaScriptObjects.get(interfaceName), interfaceName);
}
}
+ mRemovedJavaScriptObjects.clear();
stringByEvaluatingJavaScriptFromString(SearchBoxImpl.JS_BRIDGE);
}
@@ -632,12 +637,15 @@
assert obj != null;
removeJavascriptInterface(interfaceName);
- mJSInterfaceMap.put(interfaceName, obj);
+ mJavaScriptObjects.put(interfaceName, obj);
}
public void removeJavascriptInterface(String interfaceName) {
- if (mJSInterfaceMap.containsKey(interfaceName)) {
- mJSInterfaceMap.remove(interfaceName);
+ // We keep a reference to the removed object because the native side holds only a weak
+ // reference and we need to allow the object to continue to be used until the page has been
+ // navigated.
+ if (mJavaScriptObjects.containsKey(interfaceName)) {
+ mRemovedJavaScriptObjects.add(mJavaScriptObjects.remove(interfaceName));
}
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 9f632d1..7ba86a5 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1971,9 +1971,14 @@
}
/**
- * Load the given data into the WebView using a 'data' scheme URL. Content
- * loaded in this way does not have the ability to load content from the
- * network.
+ * Load the given data into the WebView using a 'data' scheme URL.
+ * <p>
+ * Note that JavaScript's same origin policy means that script running in a
+ * page loaded using this method will be unable to access content loaded
+ * using any scheme other than 'data', including 'http(s)'. To avoid this
+ * restriction, use {@link
+ * #loadDataWithBaseURL(String,String,String,String,String)
+ * loadDataWithBaseURL()} with an appropriate base URL.
* <p>
* If the value of the encoding parameter is 'base64', then the data must
* be encoded as base64. Otherwise, the data must use ASCII encoding for
@@ -2000,21 +2005,26 @@
}
/**
- * Load the given data into the WebView, use the provided URL as the base
- * URL for the content. The base URL is the URL that represents the page
- * that is loaded through this interface. As such, it is used to resolve any
- * relative URLs. The historyUrl is used for the history entry.
+ * Load the given data into the WebView, using baseUrl as the base URL for
+ * the content. The base URL is used both to resolve relative URLs and when
+ * applying JavaScript's same origin policy. The historyUrl is used for the
+ * history entry.
* <p>
* Note that content specified in this way can access local device files
* (via 'file' scheme URLs) only if baseUrl specifies a scheme other than
* 'http', 'https', 'ftp', 'ftps', 'about' or 'javascript'.
- * @param baseUrl Url to resolve relative paths with, if null defaults to
- * "about:blank"
+ * <p>
+ * If the base URL uses the data scheme, this method is equivalent to
+ * calling {@link #loadData(String,String,String) loadData()} and the
+ * historyUrl is ignored.
+ * @param baseUrl URL to use as the page's base URL. If null defaults to
+ * 'about:blank'
* @param data A String of data in the given encoding.
- * @param mimeType The MIMEType of the data. i.e. text/html. If null,
- * defaults to "text/html"
- * @param encoding The encoding of the data. i.e. utf-8, us-ascii
- * @param historyUrl URL to use as the history entry. Can be null.
+ * @param mimeType The MIMEType of the data, e.g. 'text/html'. If null,
+ * defaults to 'text/html'.
+ * @param encoding The encoding of the data.
+ * @param historyUrl URL to use as the history entry, if null defaults to
+ * 'about:blank'.
*/
public void loadDataWithBaseURL(String baseUrl, String data,
String mimeType, String encoding, String historyUrl) {
diff --git a/core/java/android/webkit/WebViewFragment.java b/core/java/android/webkit/WebViewFragment.java
index 466f174..852878b 100644
--- a/core/java/android/webkit/WebViewFragment.java
+++ b/core/java/android/webkit/WebViewFragment.java
@@ -30,6 +30,7 @@
*/
public class WebViewFragment extends Fragment {
private WebView mWebView;
+ private boolean mIsWebViewAvailable;
public WebViewFragment() {
}
@@ -40,7 +41,11 @@
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
+ if (mWebView != null) {
+ mWebView.destroy();
+ }
mWebView = new WebView(getActivity());
+ mIsWebViewAvailable = true;
return mWebView;
}
@@ -63,19 +68,31 @@
}
/**
- * Called when the view has been detached from the fragment. Destroys the WebView.
+ * Called when the WebView has been detached from the fragment.
+ * The WebView is no longer available after this time.
*/
@Override
public void onDestroyView() {
- mWebView.destroy();
- mWebView = null;
+ mIsWebViewAvailable = false;
super.onDestroyView();
}
/**
+ * Called when the fragment is no longer in use. Destroys the internal state of the WebView.
+ */
+ @Override
+ public void onDestroy() {
+ if (mWebView != null) {
+ mWebView.destroy();
+ mWebView = null;
+ }
+ super.onDestroy();
+ }
+
+ /**
* Gets the WebView.
*/
public WebView getWebView() {
- return mWebView;
+ return mIsWebViewAvailable ? mWebView : null;
}
}
diff --git a/core/java/android/widget/ActivityChooserModel.java b/core/java/android/widget/ActivityChooserModel.java
new file mode 100644
index 0000000..83f80ff
--- /dev/null
+++ b/core/java/android/widget/ActivityChooserModel.java
@@ -0,0 +1,1115 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.database.DataSetObservable;
+import android.database.DataSetObserver;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Xml;
+
+import com.android.internal.content.PackageMonitor;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * <p>
+ * This class represents a data model for choosing a component for handing a
+ * given {@link Intent}. The model is responsible for querying the system for
+ * activities that can handle the given intent and order found activities
+ * based on historical data of previous choices. The historical data is stored
+ * in an application private file. If a client does not want to have persistent
+ * choice history the file can be omitted, thus the activities will be ordered
+ * based on historical usage for the current session.
+ * <p>
+ * </p>
+ * For each backing history file there is a singleton instance of this class. Thus,
+ * several clients that specify the same history file will share the same model. Note
+ * that if multiple clients are sharing the same model they should implement semantically
+ * equivalent functionality since setting the model intent will change the found
+ * activities and they may be inconsistent with the functionality of some of the clients.
+ * For example, choosing a share activity can be implemented by a single backing
+ * model and two different views for performing the selection. If however, one of the
+ * views is used for sharing but the other for importing, for example, then each
+ * view should be backed by a separate model.
+ * </p>
+ * <p>
+ * The way clients interact with this class is as follows:
+ * </p>
+ * <p>
+ * <pre>
+ * <code>
+ * // Get a model and set it to a couple of clients with semantically similar function.
+ * ActivityChooserModel dataModel =
+ * ActivityChooserModel.get(context, "task_specific_history_file_name.xml");
+ *
+ * ActivityChooserModelClient modelClient1 = getActivityChooserModelClient1();
+ * modelClient1.setActivityChooserModel(dataModel);
+ *
+ * ActivityChooserModelClient modelClient2 = getActivityChooserModelClient2();
+ * modelClient2.setActivityChooserModel(dataModel);
+ *
+ * // Set an intent to choose a an activity for.
+ * dataModel.setIntent(intent);
+ * <pre>
+ * <code>
+ * </p>
+ * <p>
+ * <strong>Note:</strong> This class is thread safe.
+ * </p>
+ *
+ * @hide
+ */
+public class ActivityChooserModel extends DataSetObservable {
+
+ /**
+ * Client that utilizes an {@link ActivityChooserModel}.
+ */
+ public interface ActivityChooserModelClient {
+
+ /**
+ * Sets the {@link ActivityChooserModel}.
+ *
+ * @param dataModel The model.
+ */
+ public void setActivityChooserModel(ActivityChooserModel dataModel);
+ }
+
+ /**
+ * Defines a sorter that is responsible for sorting the activities
+ * based on the provided historical choices and an intent.
+ */
+ public interface ActivitySorter {
+
+ /**
+ * Sorts the <code>activities</code> in descending order of relevance
+ * based on previous history and an intent.
+ *
+ * @param intent The {@link Intent}.
+ * @param activities Activities to be sorted.
+ * @param historicalRecords Historical records.
+ */
+ // This cannot be done by a simple comparator since an Activity weight
+ // is computed from history. Note that Activity implements Comparable.
+ public void sort(Intent intent, List<Activity> activities,
+ List<HistoricalRecord> historicalRecords);
+ }
+
+ /**
+ * Flag for selecting debug mode.
+ */
+ private static final boolean DEBUG = false;
+
+ /**
+ * Tag used for logging.
+ */
+ private static final String LOG_TAG = ActivityChooserModel.class.getSimpleName();
+
+ /**
+ * The root tag in the history file.
+ */
+ private static final String TAG_HISTORICAL_RECORDS = "historical-records";
+
+ /**
+ * The tag for a record in the history file.
+ */
+ private static final String TAG_HISTORICAL_RECORD = "historical-record";
+
+ /**
+ * Attribute for the activity.
+ */
+ private static final String ATTRIBUTE_ACTIVITY = "activity";
+
+ /**
+ * Attribute for the choice time.
+ */
+ private static final String ATTRIBUTE_TIME = "time";
+
+ /**
+ * Attribute for the choice weight.
+ */
+ private static final String ATTRIBUTE_WEIGHT = "weight";
+
+ /**
+ * The default name of the choice history file.
+ */
+ public static final String DEFAULT_HISTORY_FILE_NAME =
+ "activity_choser_model_history.xml";
+
+ /**
+ * The default maximal length of the choice history.
+ */
+ public static final int DEFAULT_HISTORY_MAX_LENGTH = 50;
+
+ /**
+ * The amount with which to inflate a chosen activity when set as default.
+ */
+ private static final int DEFAULT_ACTIVITY_INFLATION = 5;
+
+ /**
+ * Default weight for a choice record.
+ */
+ private static final float DEFAULT_HISTORICAL_RECORD_WEIGHT = 1.0f;
+
+ /**
+ * The extension of the history file.
+ */
+ private static final String HISTORY_FILE_EXTENSION = ".xml";
+
+ /**
+ * An invalid item index.
+ */
+ private static final int INVALID_INDEX = -1;
+
+ /**
+ * Lock to guard the model registry.
+ */
+ private static final Object sRegistryLock = new Object();
+
+ /**
+ * This the registry for data models.
+ */
+ private static final Map<String, ActivityChooserModel> sDataModelRegistry =
+ new HashMap<String, ActivityChooserModel>();
+
+ /**
+ * Lock for synchronizing on this instance.
+ */
+ private final Object mInstanceLock = new Object();
+
+ /**
+ * List of activities that can handle the current intent.
+ */
+ private final List<Activity> mActivitys = new ArrayList<Activity>();
+
+ /**
+ * List with historical choice records.
+ */
+ private final List<HistoricalRecord> mHistoricalRecords = new ArrayList<HistoricalRecord>();
+
+ /**
+ * Monitor for added and removed packages.
+ */
+ private final PackageMonitor mPackageMonitor = new DataModelPackageMonitor();
+
+ /**
+ * Context for accessing resources.
+ */
+ private final Context mContext;
+
+ /**
+ * The name of the history file that backs this model.
+ */
+ private final String mHistoryFileName;
+
+ /**
+ * The intent for which a activity is being chosen.
+ */
+ private Intent mIntent;
+
+ /**
+ * The sorter for ordering activities based on intent and past choices.
+ */
+ private ActivitySorter mActivitySorter = new DefaultSorter();
+
+ /**
+ * The maximal length of the choice history.
+ */
+ private int mHistoryMaxSize = DEFAULT_HISTORY_MAX_LENGTH;
+
+ /**
+ * Flag whether choice history can be read. In general many clients can
+ * share the same data model and {@link #readHistoricalData()} may be called
+ * by arbitrary of them any number of times. Therefore, this class guarantees
+ * that the very first read succeeds and subsequent reads can be performed
+ * only after a call to {@link #persistHistoricalData()} followed by change
+ * of the share records.
+ */
+ private boolean mCanReadHistoricalData = true;
+
+ /**
+ * Flag whether the choice history was read. This is used to enforce that
+ * before calling {@link #persistHistoricalData()} a call to
+ * {@link #persistHistoricalData()} has been made. This aims to avoid a
+ * scenario in which a choice history file exits, it is not read yet and
+ * it is overwritten. Note that always all historical records are read in
+ * full and the file is rewritten. This is necessary since we need to
+ * purge old records that are outside of the sliding window of past choices.
+ */
+ private boolean mReadShareHistoryCalled = false;
+
+ /**
+ * Flag whether the choice records have changed. In general many clients can
+ * share the same data model and {@link #persistHistoricalData()} may be called
+ * by arbitrary of them any number of times. Therefore, this class guarantees
+ * that choice history will be persisted only if it has changed.
+ */
+ private boolean mHistoricalRecordsChanged = true;
+
+ /**
+ * Hander for scheduling work on client tread.
+ */
+ private final Handler mHandler = new Handler();
+
+ /**
+ * Gets the data model backed by the contents of the provided file with historical data.
+ * Note that only one data model is backed by a given file, thus multiple calls with
+ * the same file name will return the same model instance. If no such instance is present
+ * it is created.
+ * <p>
+ * <strong>Note:</strong> To use the default historical data file clients should explicitly
+ * pass as file name {@link #DEFAULT_HISTORY_FILE_NAME}. If no persistence of the choice
+ * history is desired clients should pass <code>null</code> for the file name. In such
+ * case a new model is returned for each invocation.
+ * </p>
+ *
+ * <p>
+ * <strong>Always use difference historical data files for semantically different actions.
+ * For example, sharing is different from importing.</strong>
+ * </p>
+ *
+ * @param context Context for loading resources.
+ * @param historyFileName File name with choice history, <code>null</code>
+ * if the model should not be backed by a file. In this case the activities
+ * will be ordered only by data from the current session.
+ *
+ * @return The model.
+ */
+ public static ActivityChooserModel get(Context context, String historyFileName) {
+ if (historyFileName == null) {
+ return new ActivityChooserModel(context, historyFileName);
+ }
+ synchronized (sRegistryLock) {
+ ActivityChooserModel dataModel = sDataModelRegistry.get(historyFileName);
+ if (dataModel == null) {
+ dataModel = new ActivityChooserModel(context, historyFileName);
+ sDataModelRegistry.put(historyFileName, dataModel);
+ }
+ return dataModel;
+ }
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param context Context for loading resources.
+ * @param historyFileName The history XML file.
+ */
+ private ActivityChooserModel(Context context, String historyFileName) {
+ mContext = context.getApplicationContext();
+ if (!TextUtils.isEmpty(historyFileName)
+ && !historyFileName.endsWith(HISTORY_FILE_EXTENSION)) {
+ mHistoryFileName = historyFileName + HISTORY_FILE_EXTENSION;
+ } else {
+ mHistoryFileName = historyFileName;
+ }
+ mPackageMonitor.register(mContext, true);
+ }
+
+ /**
+ * Sets an intent for which to choose a activity.
+ * <p>
+ * <strong>Note:</strong> Clients must set only semantically similar
+ * intents for each data model.
+ * <p>
+ *
+ * @param intent The intent.
+ */
+ public void setIntent(Intent intent) {
+ synchronized (mInstanceLock) {
+ if (mIntent == intent) {
+ return;
+ }
+ mIntent = intent;
+ loadActivitiesLocked();
+ }
+ }
+
+ /**
+ * Gets the intent for which a activity is being chosen.
+ *
+ * @return The intent.
+ */
+ public Intent getIntent() {
+ synchronized (mInstanceLock) {
+ return mIntent;
+ }
+ }
+
+ /**
+ * Gets the number of activities that can handle the intent.
+ *
+ * @return The activity count.
+ *
+ * @see #setIntent(Intent)
+ */
+ public int getActivityCount() {
+ synchronized (mInstanceLock) {
+ return mActivitys.size();
+ }
+ }
+
+ /**
+ * Gets an activity at a given index.
+ *
+ * @return The activity.
+ *
+ * @see Activity
+ * @see #setIntent(Intent)
+ */
+ public ResolveInfo getActivity(int index) {
+ synchronized (mInstanceLock) {
+ return mActivitys.get(index).resolveInfo;
+ }
+ }
+
+ /**
+ * Gets the index of a the given activity.
+ *
+ * @param activity The activity index.
+ *
+ * @return The index if found, -1 otherwise.
+ */
+ public int getActivityIndex(ResolveInfo activity) {
+ List<Activity> activities = mActivitys;
+ final int activityCount = activities.size();
+ for (int i = 0; i < activityCount; i++) {
+ Activity currentActivity = activities.get(i);
+ if (currentActivity.resolveInfo == activity) {
+ return i;
+ }
+ }
+ return INVALID_INDEX;
+ }
+
+ /**
+ * Chooses a activity to handle the current intent. This will result in
+ * adding a historical record for that action and construct intent with
+ * its component name set such that it can be immediately started by the
+ * client.
+ * <p>
+ * <strong>Note:</strong> By calling this method the client guarantees
+ * that the returned intent will be started. This intent is returned to
+ * the client solely to let additional customization before the start.
+ * </p>
+ *
+ * @return Whether adding succeeded.
+ *
+ * @see HistoricalRecord
+ */
+ public Intent chooseActivity(int index) {
+ Activity chosenActivity = mActivitys.get(index);
+ Activity defaultActivity = mActivitys.get(0);
+
+ ComponentName chosenName = new ComponentName(
+ chosenActivity.resolveInfo.activityInfo.packageName,
+ chosenActivity.resolveInfo.activityInfo.name);
+ HistoricalRecord historicalRecord = new HistoricalRecord(chosenName,
+ System.currentTimeMillis(), DEFAULT_HISTORICAL_RECORD_WEIGHT);
+ addHisoricalRecord(historicalRecord);
+
+ Intent choiceIntent = new Intent(mIntent);
+ choiceIntent.setComponent(chosenName);
+
+ return choiceIntent;
+ }
+
+ /**
+ * Gets the default activity, The default activity is defined as the one
+ * with highest rank i.e. the first one in the list of activities that can
+ * handle the intent.
+ *
+ * @return The default activity, <code>null</code> id not activities.
+ *
+ * @see #getActivity(int)
+ */
+ public ResolveInfo getDefaultActivity() {
+ synchronized (mInstanceLock) {
+ if (!mActivitys.isEmpty()) {
+ return mActivitys.get(0).resolveInfo;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Sets the default activity. The default activity is set by adding a
+ * historical record with weight high enough that this activity will
+ * become the highest ranked. Such a strategy guarantees that the default
+ * will eventually change if not used. Also the weight of the record for
+ * setting a default is inflated with a constant amount to guarantee that
+ * it will stay as default for awhile.
+ *
+ * @param index The index of the activity to set as default.
+ */
+ public void setDefaultActivity(int index) {
+ Activity newDefaultActivity = mActivitys.get(index);
+ Activity oldDefaultActivity = mActivitys.get(0);
+
+ final float weight;
+ if (oldDefaultActivity != null) {
+ // Add a record with weight enough to boost the chosen at the top.
+ weight = oldDefaultActivity.weight - newDefaultActivity.weight
+ + DEFAULT_ACTIVITY_INFLATION;
+ } else {
+ weight = DEFAULT_HISTORICAL_RECORD_WEIGHT;
+ }
+
+ ComponentName defaultName = new ComponentName(
+ newDefaultActivity.resolveInfo.activityInfo.packageName,
+ newDefaultActivity.resolveInfo.activityInfo.name);
+ HistoricalRecord historicalRecord = new HistoricalRecord(defaultName,
+ System.currentTimeMillis(), weight);
+ addHisoricalRecord(historicalRecord);
+ }
+
+ /**
+ * Reads the history data from the backing file if the latter
+ * was provided. Calling this method more than once before a call
+ * to {@link #persistHistoricalData()} has been made has no effect.
+ * <p>
+ * <strong>Note:</strong> Historical data is read asynchronously and
+ * as soon as the reading is completed any registered
+ * {@link DataSetObserver}s will be notified. Also no historical
+ * data is read until this method is invoked.
+ * <p>
+ */
+ public void readHistoricalData() {
+ synchronized (mInstanceLock) {
+ if (!mCanReadHistoricalData || !mHistoricalRecordsChanged) {
+ return;
+ }
+ mCanReadHistoricalData = false;
+ mReadShareHistoryCalled = true;
+ if (!TextUtils.isEmpty(mHistoryFileName)) {
+ AsyncTask.SERIAL_EXECUTOR.execute(new HistoryLoader());
+ }
+ }
+ }
+
+ /**
+ * Persists the history data to the backing file if the latter
+ * was provided. Calling this method before a call to {@link #readHistoricalData()}
+ * throws an exception. Calling this method more than one without choosing an
+ * activity has not effect.
+ *
+ * @throws IllegalStateException If this method is called before a call to
+ * {@link #readHistoricalData()}.
+ */
+ public void persistHistoricalData() {
+ synchronized (mInstanceLock) {
+ if (!mReadShareHistoryCalled) {
+ throw new IllegalStateException("No preceding call to #readHistoricalData");
+ }
+ if (!mHistoricalRecordsChanged) {
+ return;
+ }
+ mHistoricalRecordsChanged = false;
+ mCanReadHistoricalData = true;
+ if (!TextUtils.isEmpty(mHistoryFileName)) {
+ AsyncTask.SERIAL_EXECUTOR.execute(new HistoryPersister());
+ }
+ }
+ }
+
+ /**
+ * Sets the sorter for ordering activities based on historical data and an intent.
+ *
+ * @param activitySorter The sorter.
+ *
+ * @see ActivitySorter
+ */
+ public void setActivitySorter(ActivitySorter activitySorter) {
+ synchronized (mInstanceLock) {
+ if (mActivitySorter == activitySorter) {
+ return;
+ }
+ mActivitySorter = activitySorter;
+ sortActivities();
+ }
+ }
+
+ /**
+ * Sorts the activities based on history and an intent. If
+ * a sorter is not specified this a default implementation is used.
+ *
+ * @see #setActivitySorter(ActivitySorter)
+ */
+ private void sortActivities() {
+ synchronized (mInstanceLock) {
+ if (mActivitySorter != null && !mActivitys.isEmpty()) {
+ mActivitySorter.sort(mIntent, mActivitys,
+ Collections.unmodifiableList(mHistoricalRecords));
+ notifyChanged();
+ }
+ }
+ }
+
+ /**
+ * Sets the maximal size of the historical data. Defaults to
+ * {@link #DEFAULT_HISTORY_MAX_LENGTH}
+ * <p>
+ * <strong>Note:</strong> Setting this property will immediately
+ * enforce the specified max history size by dropping enough old
+ * historical records to enforce the desired size. Thus, any
+ * records that exceed the history size will be discarded and
+ * irreversibly lost.
+ * </p>
+ *
+ * @param historyMaxSize The max history size.
+ */
+ public void setHistoryMaxSize(int historyMaxSize) {
+ synchronized (mInstanceLock) {
+ if (mHistoryMaxSize == historyMaxSize) {
+ return;
+ }
+ mHistoryMaxSize = historyMaxSize;
+ pruneExcessiveHistoricalRecordsLocked();
+ sortActivities();
+ }
+ }
+
+ /**
+ * Gets the history max size.
+ *
+ * @return The history max size.
+ */
+ public int getHistoryMaxSize() {
+ synchronized (mInstanceLock) {
+ return mHistoryMaxSize;
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ mPackageMonitor.unregister();
+ }
+
+ /**
+ * Adds a historical record.
+ *
+ * @param historicalRecord The record to add.
+ * @return True if the record was added.
+ */
+ private boolean addHisoricalRecord(HistoricalRecord historicalRecord) {
+ synchronized (mInstanceLock) {
+ final boolean added = mHistoricalRecords.add(historicalRecord);
+ if (added) {
+ mHistoricalRecordsChanged = true;
+ pruneExcessiveHistoricalRecordsLocked();
+ sortActivities();
+ }
+ return added;
+ }
+ }
+
+ /**
+ * Prunes older excessive records to guarantee {@link #mHistoryMaxSize}.
+ */
+ private void pruneExcessiveHistoricalRecordsLocked() {
+ List<HistoricalRecord> choiceRecords = mHistoricalRecords;
+ final int pruneCount = choiceRecords.size() - mHistoryMaxSize;
+ if (pruneCount <= 0) {
+ return;
+ }
+ mHistoricalRecordsChanged = true;
+ for (int i = 0; i < pruneCount; i++) {
+ HistoricalRecord prunedRecord = choiceRecords.remove(0);
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Pruned: " + prunedRecord);
+ }
+ }
+ }
+
+ /**
+ * Loads the activities.
+ */
+ private void loadActivitiesLocked() {
+ mActivitys.clear();
+ if (mIntent != null) {
+ List<ResolveInfo> resolveInfos =
+ mContext.getPackageManager().queryIntentActivities(mIntent, 0);
+ final int resolveInfoCount = resolveInfos.size();
+ for (int i = 0; i < resolveInfoCount; i++) {
+ ResolveInfo resolveInfo = resolveInfos.get(i);
+ mActivitys.add(new Activity(resolveInfo));
+ }
+ sortActivities();
+ } else {
+ notifyChanged();
+ }
+ }
+
+ /**
+ * Prunes historical records for a package that goes away.
+ *
+ * @param packageName The name of the package that goes away.
+ */
+ private void pruneHistoricalRecordsForPackageLocked(String packageName) {
+ boolean recordsRemoved = false;
+
+ List<HistoricalRecord> historicalRecords = mHistoricalRecords;
+ for (int i = 0; i < historicalRecords.size(); i++) {
+ HistoricalRecord historicalRecord = historicalRecords.get(i);
+ String recordPackageName = historicalRecord.activity.getPackageName();
+ if (recordPackageName.equals(packageName)) {
+ historicalRecords.remove(historicalRecord);
+ recordsRemoved = true;
+ }
+ }
+
+ if (recordsRemoved) {
+ mHistoricalRecordsChanged = true;
+ sortActivities();
+ }
+ }
+
+ /**
+ * Represents a record in the history.
+ */
+ public final static class HistoricalRecord {
+
+ /**
+ * The activity name.
+ */
+ public final ComponentName activity;
+
+ /**
+ * The choice time.
+ */
+ public final long time;
+
+ /**
+ * The record weight.
+ */
+ public final float weight;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param activityName The activity component name flattened to string.
+ * @param time The time the activity was chosen.
+ * @param weight The weight of the record.
+ */
+ public HistoricalRecord(String activityName, long time, float weight) {
+ this(ComponentName.unflattenFromString(activityName), time, weight);
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param activityName The activity name.
+ * @param time The time the activity was chosen.
+ * @param weight The weight of the record.
+ */
+ public HistoricalRecord(ComponentName activityName, long time, float weight) {
+ this.activity = activityName;
+ this.time = time;
+ this.weight = weight;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((activity == null) ? 0 : activity.hashCode());
+ result = prime * result + (int) (time ^ (time >>> 32));
+ result = prime * result + Float.floatToIntBits(weight);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ HistoricalRecord other = (HistoricalRecord) obj;
+ if (activity == null) {
+ if (other.activity != null) {
+ return false;
+ }
+ } else if (!activity.equals(other.activity)) {
+ return false;
+ }
+ if (time != other.time) {
+ return false;
+ }
+ if (Float.floatToIntBits(weight) != Float.floatToIntBits(other.weight)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("[");
+ builder.append("; activity:").append(activity);
+ builder.append("; time:").append(time);
+ builder.append("; weight:").append(new BigDecimal(weight));
+ builder.append("]");
+ return builder.toString();
+ }
+ }
+
+ /**
+ * Represents an activity.
+ */
+ public final class Activity implements Comparable<Activity> {
+
+ /**
+ * The {@link ResolveInfo} of the activity.
+ */
+ public final ResolveInfo resolveInfo;
+
+ /**
+ * Weight of the activity. Useful for sorting.
+ */
+ public float weight;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param resolveInfo activity {@link ResolveInfo}.
+ */
+ public Activity(ResolveInfo resolveInfo) {
+ this.resolveInfo = resolveInfo;
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 + Float.floatToIntBits(weight);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ Activity other = (Activity) obj;
+ if (Float.floatToIntBits(weight) != Float.floatToIntBits(other.weight)) {
+ return false;
+ }
+ return true;
+ }
+
+ public int compareTo(Activity another) {
+ return Float.floatToIntBits(another.weight) - Float.floatToIntBits(weight);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("[");
+ builder.append("resolveInfo:").append(resolveInfo.toString());
+ builder.append("; weight:").append(new BigDecimal(weight));
+ builder.append("]");
+ return builder.toString();
+ }
+ }
+
+ /**
+ * Default activity sorter implementation.
+ */
+ private final class DefaultSorter implements ActivitySorter {
+ private static final float WEIGHT_DECAY_COEFFICIENT = 0.95f;
+
+ private final Map<String, Activity> mPackageNameToActivityMap =
+ new HashMap<String, Activity>();
+
+ public void sort(Intent intent, List<Activity> activities,
+ List<HistoricalRecord> historicalRecords) {
+ Map<String, Activity> packageNameToActivityMap =
+ mPackageNameToActivityMap;
+ packageNameToActivityMap.clear();
+
+ final int activityCount = activities.size();
+ for (int i = 0; i < activityCount; i++) {
+ Activity activity = activities.get(i);
+ activity.weight = 0.0f;
+ String packageName = activity.resolveInfo.activityInfo.packageName;
+ packageNameToActivityMap.put(packageName, activity);
+ }
+
+ final int lastShareIndex = historicalRecords.size() - 1;
+ float nextRecordWeight = 1;
+ for (int i = lastShareIndex; i >= 0; i--) {
+ HistoricalRecord historicalRecord = historicalRecords.get(i);
+ String packageName = historicalRecord.activity.getPackageName();
+ Activity activity = packageNameToActivityMap.get(packageName);
+ activity.weight += historicalRecord.weight * nextRecordWeight;
+ nextRecordWeight = nextRecordWeight * WEIGHT_DECAY_COEFFICIENT;
+ }
+
+ Collections.sort(activities);
+
+ if (DEBUG) {
+ for (int i = 0; i < activityCount; i++) {
+ Log.i(LOG_TAG, "Sorted: " + activities.get(i));
+ }
+ }
+ }
+ }
+
+ /**
+ * Command for reading the historical records from a file off the UI thread.
+ */
+ private final class HistoryLoader implements Runnable {
+
+ public void run() {
+ FileInputStream fis = null;
+ try {
+ fis = mContext.openFileInput(mHistoryFileName);
+ } catch (FileNotFoundException fnfe) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Could not open historical records file: " + mHistoryFileName);
+ }
+ return;
+ }
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(fis, null);
+
+ int type = XmlPullParser.START_DOCUMENT;
+ while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
+ type = parser.next();
+ }
+
+ if (!TAG_HISTORICAL_RECORDS.equals(parser.getName())) {
+ throw new XmlPullParserException("Share records file does not start with "
+ + TAG_HISTORICAL_RECORDS + " tag.");
+ }
+
+ List<HistoricalRecord> readRecords = new ArrayList<HistoricalRecord>();
+
+ while (true) {
+ type = parser.next();
+ if (type == XmlPullParser.END_DOCUMENT) {
+ break;
+ }
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ String nodeName = parser.getName();
+ if (!TAG_HISTORICAL_RECORD.equals(nodeName)) {
+ throw new XmlPullParserException("Share records file not well-formed.");
+ }
+
+ String activity = parser.getAttributeValue(null, ATTRIBUTE_ACTIVITY);
+ final long time =
+ Long.parseLong(parser.getAttributeValue(null, ATTRIBUTE_TIME));
+ final float weight =
+ Float.parseFloat(parser.getAttributeValue(null, ATTRIBUTE_WEIGHT));
+
+ HistoricalRecord readRecord = new HistoricalRecord(activity, time,
+ weight);
+ readRecords.add(readRecord);
+
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Read " + readRecord.toString());
+ }
+ }
+
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Read " + readRecords.size() + " historical records.");
+ }
+
+ synchronized (mInstanceLock) {
+ Set<HistoricalRecord> uniqueShareRecords =
+ new LinkedHashSet<HistoricalRecord>(readRecords);
+
+ // Make sure no duplicates. Example: Read a file with
+ // one record, add one record, persist the two records,
+ // add a record, read the persisted records - the
+ // read two records should not be added again.
+ List<HistoricalRecord> historicalRecords = mHistoricalRecords;
+ final int historicalRecordsCount = historicalRecords.size();
+ for (int i = historicalRecordsCount - 1; i >= 0; i--) {
+ HistoricalRecord historicalRecord = historicalRecords.get(i);
+ uniqueShareRecords.add(historicalRecord);
+ }
+
+ if (historicalRecords.size() == uniqueShareRecords.size()) {
+ return;
+ }
+
+ // Make sure the oldest records go to the end.
+ historicalRecords.clear();
+ historicalRecords.addAll(uniqueShareRecords);
+
+ mHistoricalRecordsChanged = true;
+
+ // Do this on the client thread since the client may be on the UI
+ // thread, wait for data changes which happen during sorting, and
+ // perform UI modification based on the data change.
+ mHandler.post(new Runnable() {
+ public void run() {
+ pruneExcessiveHistoricalRecordsLocked();
+ sortActivities();
+ }
+ });
+ }
+ } catch (XmlPullParserException xppe) {
+ Log.e(LOG_TAG, "Error reading historical recrod file: " + mHistoryFileName, xppe);
+ } catch (IOException ioe) {
+ Log.e(LOG_TAG, "Error reading historical recrod file: " + mHistoryFileName, ioe);
+ } finally {
+ if (fis != null) {
+ try {
+ fis.close();
+ } catch (IOException ioe) {
+ /* ignore */
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Command for persisting the historical records to a file off the UI thread.
+ */
+ private final class HistoryPersister implements Runnable {
+
+ public void run() {
+ FileOutputStream fos = null;
+ List<HistoricalRecord> records = null;
+
+ synchronized (mInstanceLock) {
+ records = new ArrayList<HistoricalRecord>(mHistoricalRecords);
+ }
+
+ try {
+ fos = mContext.openFileOutput(mHistoryFileName, Context.MODE_PRIVATE);
+ } catch (FileNotFoundException fnfe) {
+ Log.e(LOG_TAG, "Error writing historical recrod file: " + mHistoryFileName, fnfe);
+ return;
+ }
+
+ XmlSerializer serializer = Xml.newSerializer();
+
+ try {
+ serializer.setOutput(fos, null);
+ serializer.startDocument("UTF-8", true);
+ serializer.startTag(null, TAG_HISTORICAL_RECORDS);
+
+ final int recordCount = records.size();
+ for (int i = 0; i < recordCount; i++) {
+ HistoricalRecord record = records.remove(0);
+ serializer.startTag(null, TAG_HISTORICAL_RECORD);
+ serializer.attribute(null, ATTRIBUTE_ACTIVITY, record.activity.flattenToString());
+ serializer.attribute(null, ATTRIBUTE_TIME, String.valueOf(record.time));
+ serializer.attribute(null, ATTRIBUTE_WEIGHT, String.valueOf(record.weight));
+ serializer.endTag(null, TAG_HISTORICAL_RECORD);
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Wrote " + record.toString());
+ }
+ }
+
+ serializer.endTag(null, TAG_HISTORICAL_RECORDS);
+ serializer.endDocument();
+
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Wrote " + recordCount + " historical records.");
+ }
+ } catch (IllegalArgumentException iae) {
+ Log.e(LOG_TAG, "Error writing historical recrod file: " + mHistoryFileName, iae);
+ } catch (IllegalStateException ise) {
+ Log.e(LOG_TAG, "Error writing historical recrod file: " + mHistoryFileName, ise);
+ } catch (IOException ioe) {
+ Log.e(LOG_TAG, "Error writing historical recrod file: " + mHistoryFileName, ioe);
+ } finally {
+ if (fos != null) {
+ try {
+ fos.close();
+ } catch (IOException e) {
+ /* ignore */
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Keeps in sync the historical records and activities with the installed applications.
+ */
+ private final class DataModelPackageMonitor extends PackageMonitor {
+
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ synchronized (mInstanceLock) {
+ loadActivitiesLocked();
+ }
+ }
+
+ @Override
+ public void onPackageAppeared(String packageName, int reason) {
+ synchronized (mInstanceLock) {
+ loadActivitiesLocked();
+ }
+ }
+
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ synchronized (mInstanceLock) {
+ pruneHistoricalRecordsForPackageLocked(packageName);
+ loadActivitiesLocked();
+ }
+ }
+
+ @Override
+ public void onPackageDisappeared(String packageName, int reason) {
+ synchronized (mInstanceLock) {
+ pruneHistoricalRecordsForPackageLocked(packageName);
+ loadActivitiesLocked();
+ }
+ }
+ }
+}
diff --git a/core/java/android/widget/ActivityChooserView.java b/core/java/android/widget/ActivityChooserView.java
new file mode 100644
index 0000000..2fe8162
--- /dev/null
+++ b/core/java/android/widget/ActivityChooserView.java
@@ -0,0 +1,765 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.app.AlertDialog;
+import android.app.AlertDialog.Builder;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.TypedArray;
+import android.database.DataSetObserver;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.os.Debug;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ActivityChooserModel.ActivityChooserModelClient;
+
+import com.android.internal.R;
+
+/**
+ * This class is a view for choosing an activity for handling a given {@link Intent}.
+ * <p>
+ * The view is composed of two adjacent buttons:
+ * <ul>
+ * <li>
+ * The left button is an immediate action and allows one click activity choosing.
+ * Tapping this button immediately executes the intent without requiring any further
+ * user input. Long press on this button shows a popup for changing the default
+ * activity.
+ * </li>
+ * <li>
+ * The right button is an overflow action and provides an optimized menu
+ * of additional activities. Tapping this button shows a popup anchored to this
+ * view, listing the most frequently used activities. This list is initially
+ * limited to a small number of items in frequency used order. The last item,
+ * "Show all..." serves as an affordance to display all available activities.
+ * </li>
+ * </ul>
+ * </p>
+ * </p>
+ * This view is backed by a {@link ActivityChooserModel}. Calling {@link #showPopup()}
+ * while this view is attached to the view hierarchy will show a popup with
+ * activities while if the view is not attached it will show a dialog.
+ * </p>
+ *
+ * @hide
+ */
+public class ActivityChooserView extends ViewGroup implements ActivityChooserModelClient {
+
+ /**
+ * An adapter for displaying the activities in an {@link AdapterView}.
+ */
+ private final ActivityChooserViewAdapter mAdapter;
+
+ /**
+ * Implementation of various interfaces to avoid publishing them in the APIs.
+ */
+ private final Callbacks mCallbacks;
+
+ /**
+ * The content of this view.
+ */
+ private final LinearLayout mActivityChooserContent;
+
+ /**
+ * The expand activities action button;
+ */
+ private final ImageButton mExpandActivityOverflowButton;
+
+ /**
+ * The default activities action button;
+ */
+ private final ImageButton mDefaultActionButton;
+
+ /**
+ * The header for handlers list.
+ */
+ private final View mListHeaderView;
+
+ /**
+ * The footer for handlers list.
+ */
+ private final View mListFooterView;
+
+ /**
+ * The title of the header view.
+ */
+ private TextView mListHeaderViewTitle;
+
+ /**
+ * The title for expanding the activities list.
+ */
+ private final String mListHeaderViewTitleSelectDefault;
+
+ /**
+ * The title if no activity exist.
+ */
+ private final String mListHeaderViewTitleNoActivities;
+
+ /**
+ * Popup window for showing the activity overflow list.
+ */
+ private ListPopupWindow mListPopupWindow;
+
+ /**
+ * Alert dialog for showing the activity overflow list.
+ */
+ private AlertDialog mAlertDialog;
+
+ /**
+ * Listener for the dismissal of the popup/alert.
+ */
+ private PopupWindow.OnDismissListener mOnDismissListener;
+
+ /**
+ * Flag whether a default activity currently being selected.
+ */
+ private boolean mIsSelectingDefaultActivity;
+
+ /**
+ * The count of activities in the popup.
+ */
+ private int mInitialActivityCount = ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_DEFAULT;
+
+ /**
+ * Flag whether this view is attached to a window.
+ */
+ private boolean mIsAttachedToWindow;
+
+ /**
+ * Flag whether this view is showing an alert dialog.
+ */
+ private boolean mIsShowingAlertDialog;
+
+ /**
+ * Flag whether this view is showing a popup window.
+ */
+ private boolean mIsShowingPopuWindow;
+
+ /**
+ * Create a new instance.
+ *
+ * @param context The application environment.
+ */
+ public ActivityChooserView(Context context) {
+ this(context, null);
+ }
+
+ /**
+ * Create a new instance.
+ *
+ * @param context The application environment.
+ * @param attrs A collection of attributes.
+ */
+ public ActivityChooserView(Context context, AttributeSet attrs) {
+ this(context, attrs, R.attr.actionButtonStyle);
+ }
+
+ /**
+ * Create a new instance.
+ *
+ * @param context The application environment.
+ * @param attrs A collection of attributes.
+ * @param defStyle The default style to apply to this view.
+ */
+ public ActivityChooserView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ TypedArray attributesArray = context.obtainStyledAttributes(attrs,
+ R.styleable.ActivityChooserView, defStyle, 0);
+
+ mInitialActivityCount = attributesArray.getInt(
+ R.styleable.ActivityChooserView_initialActivityCount,
+ ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_DEFAULT);
+
+ Drawable expandActivityOverflowButtonDrawable = attributesArray.getDrawable(
+ R.styleable.ActivityChooserView_expandActivityOverflowButtonDrawable);
+
+ LayoutInflater inflater = (LayoutInflater) context.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.activity_chooser_view, this, true);
+
+ mCallbacks = new Callbacks();
+
+ mActivityChooserContent = (LinearLayout) findViewById(R.id.activity_chooser_view_content);
+
+ mDefaultActionButton = (ImageButton) findViewById(R.id.default_activity_button);
+ mDefaultActionButton.setOnClickListener(mCallbacks);
+ mDefaultActionButton.setOnLongClickListener(mCallbacks);
+
+ mExpandActivityOverflowButton = (ImageButton) findViewById(R.id.expand_activities_button);
+ mExpandActivityOverflowButton.setOnClickListener(mCallbacks);
+ mExpandActivityOverflowButton.setBackgroundDrawable(expandActivityOverflowButtonDrawable);
+
+ mListHeaderView = inflater.inflate(R.layout.activity_chooser_list_header, null);
+ mListFooterView = inflater.inflate(R.layout.activity_chooser_list_footer, null);
+
+ mListHeaderViewTitle = (TextView) mListHeaderView.findViewById(R.id.title);
+ mListHeaderViewTitleSelectDefault = context.getString(
+ R.string.activity_chooser_view_select_default);
+ mListHeaderViewTitleNoActivities = context.getString(
+ R.string.activity_chooser_view_no_activities);
+
+ mAdapter = new ActivityChooserViewAdapter();
+ mAdapter.registerDataSetObserver(new DataSetObserver() {
+ @Override
+ public void onChanged() {
+ super.onChanged();
+ updateButtons();
+ }
+ });
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setActivityChooserModel(ActivityChooserModel dataModel) {
+ mAdapter.setDataModel(dataModel);
+ if (isShowingPopup()) {
+ dismissPopup();
+ showPopup();
+ }
+ }
+
+ /**
+ * Sets the background for the button that expands the activity
+ * overflow list.
+ *
+ * <strong>Note:</strong> Clients would like to set this drawable
+ * as a clue about the action the chosen activity will perform. For
+ * example, if share activity is to be chosen the drawable should
+ * give a clue that sharing is to be performed.
+ *
+ * @param drawable The drawable.
+ */
+ public void setExpandActivityOverflowButtonDrawable(Drawable drawable) {
+ mExpandActivityOverflowButton.setBackgroundDrawable(drawable);
+ }
+
+ /**
+ * Shows the popup window with activities.
+ *
+ * @return True if the popup was shown, false if already showing.
+ */
+ public boolean showPopup() {
+ if (isShowingPopup()) {
+ return false;
+ }
+ mIsSelectingDefaultActivity = false;
+ showPopupUnchecked(mInitialActivityCount);
+ return true;
+ }
+
+ /**
+ * Shows the popup no matter if it was already showing.
+ *
+ * @param maxActivityCount The max number of activities to display.
+ */
+ private void showPopupUnchecked(int maxActivityCount) {
+ mAdapter.setMaxActivityCount(maxActivityCount);
+ if (mIsSelectingDefaultActivity) {
+ if (mAdapter.getActivityCount() > 0) {
+ mListHeaderViewTitle.setText(mListHeaderViewTitleSelectDefault);
+ } else {
+ mListHeaderViewTitle.setText(mListHeaderViewTitleNoActivities);
+ }
+ mAdapter.setHeaderView(mListHeaderView);
+ } else {
+ mAdapter.setHeaderView(null);
+ }
+
+ if (mAdapter.getActivityCount() > maxActivityCount + 1) {
+ mAdapter.setFooterView(mListFooterView);
+ } else {
+ mAdapter.setFooterView(null);
+ }
+
+ if (!mIsAttachedToWindow || mIsShowingAlertDialog) {
+ AlertDialog alertDialog = getAlertDilalog();
+ if (!alertDialog.isShowing()) {
+ alertDialog.setCustomTitle(this);
+ alertDialog.show();
+ mIsShowingAlertDialog = true;
+ }
+ } else {
+ ListPopupWindow popupWindow = getListPopupWindow();
+ if (!popupWindow.isShowing()) {
+ popupWindow.setContentWidth(mAdapter.measureContentWidth());
+ popupWindow.show();
+ mIsShowingPopuWindow = true;
+ }
+ }
+ }
+
+ /**
+ * Dismisses the popup window with activities.
+ *
+ * @return True if dismissed, false if already dismissed.
+ */
+ public boolean dismissPopup() {
+ if (!isShowingPopup()) {
+ return false;
+ }
+ if (mIsShowingAlertDialog) {
+ getAlertDilalog().dismiss();
+ } else if (mIsShowingPopuWindow) {
+ getListPopupWindow().dismiss();
+ }
+ return true;
+ }
+
+ /**
+ * Gets whether the popup window with activities is shown.
+ *
+ * @return True if the popup is shown.
+ */
+ public boolean isShowingPopup() {
+ if (mIsShowingAlertDialog) {
+ return getAlertDilalog().isShowing();
+ } else if (mIsShowingPopuWindow) {
+ return getListPopupWindow().isShowing();
+ }
+ return false;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ ActivityChooserModel dataModel = mAdapter.getDataModel();
+ if (dataModel != null) {
+ dataModel.readHistoricalData();
+ }
+ mIsAttachedToWindow = true;
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ ActivityChooserModel dataModel = mAdapter.getDataModel();
+ if (dataModel != null) {
+ dataModel.persistHistoricalData();
+ }
+ mIsAttachedToWindow = false;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ mActivityChooserContent.measure(widthMeasureSpec, heightMeasureSpec);
+ setMeasuredDimension(mActivityChooserContent.getMeasuredWidth(),
+ mActivityChooserContent.getMeasuredHeight());
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ mActivityChooserContent.layout(left, top, right, bottom);
+ if (mIsShowingPopuWindow) {
+ if (isShown()) {
+ showPopupUnchecked(mAdapter.getMaxActivityCount());
+ } else {
+ dismissPopup();
+ }
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ mActivityChooserContent.onDraw(canvas);
+ }
+
+ public ActivityChooserModel getDataModel() {
+ return mAdapter.getDataModel();
+ }
+
+ /**
+ * Sets a listener to receive a callback when the popup is dismissed.
+ *
+ * @param listener The listener to be notified.
+ */
+ public void setOnDismissListener(PopupWindow.OnDismissListener listener) {
+ mOnDismissListener = listener;
+ }
+
+ /**
+ * Sets the initial count of items shown in the activities popup
+ * i.e. the items before the popup is expanded. This is an upper
+ * bound since it is not guaranteed that such number of intent
+ * handlers exist.
+ *
+ * @param itemCount The initial popup item count.
+ */
+ public void setInitialActivityCount(int itemCount) {
+ mInitialActivityCount = itemCount;
+ }
+
+ /**
+ * Gets the list popup window which is lazily initialized.
+ *
+ * @return The popup.
+ */
+ private ListPopupWindow getListPopupWindow() {
+ if (mListPopupWindow == null) {
+ mListPopupWindow = new ListPopupWindow(getContext());
+ mListPopupWindow.setAdapter(mAdapter);
+ mListPopupWindow.setAnchorView(ActivityChooserView.this);
+ mListPopupWindow.setModal(true);
+ mListPopupWindow.setOnItemClickListener(mCallbacks);
+ mListPopupWindow.setOnDismissListener(mCallbacks);
+ }
+ return mListPopupWindow;
+ }
+
+ /**
+ * Gets the alert dialog which is lazily initialized.
+ *
+ * @return The popup.
+ */
+ private AlertDialog getAlertDilalog() {
+ if (mAlertDialog == null) {
+ Builder builder = new Builder(getContext());
+ builder.setAdapter(mAdapter, null);
+ mAlertDialog = builder.create();
+ mAlertDialog.getListView().setOnItemClickListener(mCallbacks);
+ mAlertDialog.setOnDismissListener(mCallbacks);
+ }
+ return mAlertDialog;
+ }
+
+ /**
+ * Updates the buttons state.
+ */
+ private void updateButtons() {
+ final int activityCount = mAdapter.getActivityCount();
+ if (activityCount > 0) {
+ mDefaultActionButton.setVisibility(VISIBLE);
+ if (mAdapter.getCount() > 0) {
+ mExpandActivityOverflowButton.setEnabled(true);
+ } else {
+ mExpandActivityOverflowButton.setEnabled(false);
+ }
+ ResolveInfo activity = mAdapter.getDefaultActivity();
+ PackageManager packageManager = mContext.getPackageManager();
+ mDefaultActionButton.setBackgroundDrawable(activity.loadIcon(packageManager));
+ } else {
+ mDefaultActionButton.setVisibility(View.INVISIBLE);
+ mExpandActivityOverflowButton.setEnabled(false);
+ }
+ }
+
+ /**
+ * Interface implementation to avoid publishing them in the APIs.
+ */
+ private class Callbacks implements AdapterView.OnItemClickListener,
+ View.OnClickListener, View.OnLongClickListener, PopupWindow.OnDismissListener,
+ DialogInterface.OnDismissListener {
+
+ // AdapterView#OnItemClickListener
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ ActivityChooserViewAdapter adapter = (ActivityChooserViewAdapter) parent.getAdapter();
+ final int itemViewType = adapter.getItemViewType(position);
+ switch (itemViewType) {
+ case ActivityChooserViewAdapter.ITEM_VIEW_TYPE_HEADER: {
+ /* do nothing */
+ } break;
+ case ActivityChooserViewAdapter.ITEM_VIEW_TYPE_FOOTER: {
+ showPopupUnchecked(ActivityChooserViewAdapter.MAX_ACTIVITY_COUNT_UNLIMITED);
+ } break;
+ case ActivityChooserViewAdapter.ITEM_VIEW_TYPE_ACTIVITY: {
+ dismissPopup();
+ if (mIsSelectingDefaultActivity) {
+ mAdapter.getDataModel().setDefaultActivity(position);
+ } else {
+ // The first item in the model is default action => adjust index
+ Intent launchIntent = mAdapter.getDataModel().chooseActivity(position + 1);
+ mContext.startActivity(launchIntent);
+ }
+ } break;
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ // View.OnClickListener
+ public void onClick(View view) {
+ if (view == mDefaultActionButton) {
+ dismissPopup();
+ ResolveInfo defaultActivity = mAdapter.getDefaultActivity();
+ final int index = mAdapter.getDataModel().getActivityIndex(defaultActivity);
+ Intent launchIntent = mAdapter.getDataModel().chooseActivity(index);
+ mContext.startActivity(launchIntent);
+ } else if (view == mExpandActivityOverflowButton) {
+ mIsSelectingDefaultActivity = false;
+ showPopupUnchecked(mInitialActivityCount);
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ // OnLongClickListener#onLongClick
+ @Override
+ public boolean onLongClick(View view) {
+ if (view == mDefaultActionButton) {
+ if (mAdapter.getCount() > 0) {
+ mIsSelectingDefaultActivity = true;
+ showPopupUnchecked(mInitialActivityCount);
+ }
+ } else {
+ throw new IllegalArgumentException();
+ }
+ return true;
+ }
+
+ // PopUpWindow.OnDismissListener#onDismiss
+ public void onDismiss() {
+ mIsShowingPopuWindow = false;
+ notifyOnDismissListener();
+ }
+
+ // DialogInterface.OnDismissListener#onDismiss
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ mIsShowingAlertDialog = false;
+ AlertDialog alertDialog = (AlertDialog) dialog;
+ alertDialog.setCustomTitle(null);
+ notifyOnDismissListener();
+ }
+
+ private void notifyOnDismissListener() {
+ if (mOnDismissListener != null) {
+ mOnDismissListener.onDismiss();
+ }
+ }
+ }
+
+ /**
+ * Adapter for backing the list of activities shown in the popup.
+ */
+ private class ActivityChooserViewAdapter extends BaseAdapter {
+
+ public static final int MAX_ACTIVITY_COUNT_UNLIMITED = Integer.MAX_VALUE;
+
+ public static final int MAX_ACTIVITY_COUNT_DEFAULT = 4;
+
+ private static final int ITEM_VIEW_TYPE_HEADER = 0;
+
+ private static final int ITEM_VIEW_TYPE_ACTIVITY = 1;
+
+ private static final int ITEM_VIEW_TYPE_FOOTER = 2;
+
+ private static final int ITEM_VIEW_TYPE_COUNT = 3;
+
+ private final DataSetObserver mDataSetOberver = new DataSetObserver() {
+
+ @Override
+ public void onChanged() {
+ super.onChanged();
+ notifyDataSetChanged();
+ }
+ @Override
+ public void onInvalidated() {
+ super.onInvalidated();
+ notifyDataSetInvalidated();
+ }
+ };
+
+ private ActivityChooserModel mDataModel;
+
+ private int mMaxActivityCount = MAX_ACTIVITY_COUNT_DEFAULT;
+
+ private ResolveInfo mDefaultActivity;
+
+ private View mHeaderView;
+
+ private View mFooterView;
+
+ public void setDataModel(ActivityChooserModel dataModel) {
+ mDataModel = dataModel;
+ mDataModel.registerObserver(mDataSetOberver);
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public void notifyDataSetChanged() {
+ if (mDataModel.getActivityCount() > 0) {
+ mDefaultActivity = mDataModel.getDefaultActivity();
+ } else {
+ mDefaultActivity = null;
+ }
+ super.notifyDataSetChanged();
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (mHeaderView != null && position == 0) {
+ return ITEM_VIEW_TYPE_HEADER;
+ } else if (mFooterView != null && position == getCount() - 1) {
+ return ITEM_VIEW_TYPE_FOOTER;
+ } else {
+ return ITEM_VIEW_TYPE_ACTIVITY;
+ }
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return ITEM_VIEW_TYPE_COUNT;
+ }
+
+ public int getCount() {
+ int count = 0;
+ int activityCount = mDataModel.getActivityCount();
+ if (activityCount > 0) {
+ activityCount--;
+ }
+ count = Math.min(activityCount, mMaxActivityCount);
+ if (mHeaderView != null) {
+ count++;
+ }
+ if (mFooterView != null) {
+ count++;
+ }
+ return count;
+ }
+
+ public Object getItem(int position) {
+ final int itemViewType = getItemViewType(position);
+ switch (itemViewType) {
+ case ITEM_VIEW_TYPE_HEADER:
+ return mHeaderView;
+ case ITEM_VIEW_TYPE_FOOTER:
+ return mFooterView;
+ case ITEM_VIEW_TYPE_ACTIVITY:
+ int targetIndex = (mHeaderView == null) ? position : position - 1;
+ if (mDefaultActivity != null) {
+ targetIndex++;
+ }
+ return mDataModel.getActivity(targetIndex);
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ final int itemViewType = getItemViewType(position);
+ switch (itemViewType) {
+ case ITEM_VIEW_TYPE_HEADER:
+ return false;
+ case ITEM_VIEW_TYPE_FOOTER:
+ case ITEM_VIEW_TYPE_ACTIVITY:
+ return true;
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final int itemViewType = getItemViewType(position);
+ switch (itemViewType) {
+ case ITEM_VIEW_TYPE_HEADER:
+ return mHeaderView;
+ case ITEM_VIEW_TYPE_FOOTER:
+ return mFooterView;
+ case ITEM_VIEW_TYPE_ACTIVITY:
+ if (convertView == null || convertView.getId() != R.id.list_item) {
+ convertView = LayoutInflater.from(getContext()).inflate(
+ R.layout.activity_chooser_view_list_item, parent, false);
+ }
+ PackageManager packageManager = mContext.getPackageManager();
+ // Set the icon
+ ImageView iconView = (ImageView) convertView.findViewById(R.id.icon);
+ ResolveInfo activity = (ResolveInfo) getItem(position);
+ iconView.setBackgroundDrawable(activity.loadIcon(packageManager));
+ // Set the title.
+ TextView titleView = (TextView) convertView.findViewById(R.id.title);
+ titleView.setText(activity.loadLabel(packageManager));
+ return convertView;
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ public int measureContentWidth() {
+ // The user may have specified some of the target not to be show but we
+ // want to measure all of them since after expansion they should fit.
+ final int oldMaxActivityCount = mMaxActivityCount;
+ mMaxActivityCount = MAX_ACTIVITY_COUNT_UNLIMITED;
+
+ int contentWidth = 0;
+ View itemView = null;
+
+ final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ final int count = getCount();
+
+ for (int i = 0; i < count; i++) {
+ itemView = getView(i, itemView, null);
+ itemView.measure(widthMeasureSpec, heightMeasureSpec);
+ contentWidth = Math.max(contentWidth, itemView.getMeasuredWidth());
+ }
+
+ mMaxActivityCount = oldMaxActivityCount;
+
+ return contentWidth;
+ }
+
+ public void setMaxActivityCount(int maxActivityCount) {
+ if (mMaxActivityCount != maxActivityCount) {
+ mMaxActivityCount = maxActivityCount;
+ notifyDataSetChanged();
+ }
+ }
+
+ public ResolveInfo getDefaultActivity() {
+ return mDefaultActivity;
+ }
+
+ public void setHeaderView(View headerView) {
+ if (mHeaderView != headerView) {
+ mHeaderView = headerView;
+ notifyDataSetChanged();
+ }
+ }
+
+ public void setFooterView(View footerView) {
+ if (mFooterView != footerView) {
+ mFooterView = footerView;
+ notifyDataSetChanged();
+ }
+ }
+
+ public int getActivityCount() {
+ return mDataModel.getActivityCount();
+ }
+
+ public int getMaxActivityCount() {
+ return mMaxActivityCount;
+ }
+
+ public ActivityChooserModel getDataModel() {
+ return mDataModel;
+ }
+ }
+}
diff --git a/core/java/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java
new file mode 100644
index 0000000..d6e426f
--- /dev/null
+++ b/core/java/android/widget/ShareActionProvider.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.util.TypedValue;
+import android.view.ActionProvider;
+import android.view.MenuItem;
+import android.view.View;
+
+import com.android.internal.R;
+
+/**
+ * This is a provider for a share action. It is responsible for creating views
+ * that enable data sharing and also to perform a default action for showing
+ * a share dialog.
+ * <p>
+ * Here is how to use the action provider with custom backing file in a {@link MenuItem}:
+ * </p>
+ * <p>
+ * <pre>
+ * <code>
+ * // In Activity#onCreateOptionsMenu
+ * public boolean onCreateOptionsMenu(Menu menu) {
+ * // Get the menu item.
+ * MenuItem menuItem = menu.findItem(R.id.my_menu_item);
+ * // Get the provider and hold onto it to set/change the share intent.
+ * mShareActionProvider = (ShareActionProvider) menuItem.getActionProvider();
+ * // Set history different from the default before getting the action
+ * // view since a call to {@link MenuItem#getActionView() MenuItem.getActionView()} calls
+ * // {@link ActionProvider#onCreateActionView()} which uses the backing file name. Omit this
+ * // line if using the default share history file is desired.
+ * mShareActionProvider.setShareHistoryFileName("custom_share_history.xml");
+ * // Get the action view and hold onto it to set the share intent.
+ * mActionView = menuItem.getActionView();
+ * . . .
+ * }
+ *
+ * // Somewhere in the application.
+ * public void doShare(Intent shareIntent) {
+ * // When you want to share set the share intent.
+ * mShareActionProvider.setShareIntent(mActionView, shareIntent);
+ * }
+ * </pre>
+ * </code>
+ * </p>
+ * <p>
+ * <strong>Note:</strong> While the sample snippet demonstrates how to use this provider
+ * in the context of a menu item, the use of the provider is not limited to menu items.
+ * </p>
+ *
+ * @see ActionProvider
+ */
+public class ShareActionProvider extends ActionProvider {
+
+ /**
+ * The default name for storing share history.
+ */
+ public static final String DEFAULT_SHARE_HISTORY_FILE_NAME = "share_history.xml";
+
+ private final Context mContext;
+ private String mShareHistoryFileName = DEFAULT_SHARE_HISTORY_FILE_NAME;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param context Context for accessing resources.
+ */
+ public ShareActionProvider(Context context) {
+ super(context);
+ mContext = context;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public View onCreateActionView() {
+ ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName);
+ ActivityChooserView activityChooserView = new ActivityChooserView(mContext);
+ activityChooserView.setActivityChooserModel(dataModel);
+ TypedValue outTypedValue = new TypedValue();
+ mContext.getTheme().resolveAttribute(R.attr.actionModeShareDrawable, outTypedValue, true);
+ Drawable drawable = mContext.getResources().getDrawable(outTypedValue.resourceId);
+ activityChooserView.setExpandActivityOverflowButtonDrawable(drawable);
+ return activityChooserView;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onPerformDefaultAction(View actionView) {
+ if (actionView instanceof ActivityChooserView) {
+ ActivityChooserView activityChooserView = (ActivityChooserView) actionView;
+ activityChooserView.showPopup();
+ } else {
+ throw new IllegalArgumentException("actionView not instance of ActivityChooserView");
+ }
+ }
+
+ /**
+ * Sets the file name of a file for persisting the share history which
+ * history will be used for ordering share targets. This file will be used
+ * for all view created by {@link #onCreateActionView()}. Defaults to
+ * {@link #DEFAULT_SHARE_HISTORY_FILE_NAME}. Set to <code>null</code>
+ * if share history should not be persisted between sessions.
+ * <p>
+ * <strong>Note:</strong> The history file name can be set any time, however
+ * only the action views created by {@link #onCreateActionView()} after setting
+ * the file name will be backed by the provided file.
+ * <p>
+ *
+ * @param shareHistoryFile The share history file name.
+ */
+ public void setShareHistoryFileName(String shareHistoryFile) {
+ mShareHistoryFileName = shareHistoryFile;
+ }
+
+ /**
+ * Sets an intent with information about the share action. Here is a
+ * sample for constructing a share intent:
+ * <p>
+ * <pre>
+ * <code>
+ * Intent shareIntent = new Intent(Intent.ACTION_SEND);
+ * shareIntent.setType("image/*");
+ * Uri uri = Uri.fromFile(new File(getFilesDir(), "foo.jpg"));
+ * shareIntent.putExtra(Intent.EXTRA_STREAM, uri.toString());
+ * </pre>
+ * </code>
+ * </p>
+ *
+ * @param actionView An action view created by {@link #onCreateActionView()}.
+ * @param shareIntent The share intent.
+ *
+ * @see Intent#ACTION_SEND
+ * @see Intent#ACTION_SEND_MULTIPLE
+ */
+ public void setShareIntent(View actionView, Intent shareIntent) {
+ if (actionView instanceof ActivityChooserView) {
+ ActivityChooserView activityChooserView = (ActivityChooserView) actionView;
+ activityChooserView.getDataModel().setIntent(shareIntent);
+ } else {
+ throw new IllegalArgumentException("actionView not instance of ActivityChooserView");
+ }
+ }
+}
diff --git a/vpn/java/android/net/vpn/VpnProfile.aidl b/core/java/com/android/internal/net/LegacyVpnInfo.aidl
similarity index 75%
rename from vpn/java/android/net/vpn/VpnProfile.aidl
rename to core/java/com/android/internal/net/LegacyVpnInfo.aidl
index edeaef0..0ca2627 100644
--- a/vpn/java/android/net/vpn/VpnProfile.aidl
+++ b/core/java/com/android/internal/net/LegacyVpnInfo.aidl
@@ -1,11 +1,11 @@
/*
- * Copyright (C) 2009, The Android Open Source Project
+ * Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net.vpn;
+package com.android.internal.net;
-parcelable VpnProfile;
+parcelable LegacyVpnInfo;
diff --git a/core/java/com/android/internal/net/LegacyVpnInfo.java b/core/java/com/android/internal/net/LegacyVpnInfo.java
new file mode 100644
index 0000000..b620abac
--- /dev/null
+++ b/core/java/com/android/internal/net/LegacyVpnInfo.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.net;
+
+import android.app.PendingIntent;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A simple container used to carry information of the ongoing legacy VPN.
+ * Internal use only.
+ *
+ * @hide
+ */
+public class LegacyVpnInfo implements Parcelable {
+ public static final int STATE_DISCONNECTED = 0;
+ public static final int STATE_INITIALIZING = 1;
+ public static final int STATE_CONNECTING = 2;
+ public static final int STATE_CONNECTED = 3;
+ public static final int STATE_TIMEOUT = 4;
+ public static final int STATE_FAILED = 5;
+
+ public String key;
+ public int state = -1;
+ public PendingIntent intent;
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(key);
+ out.writeInt(state);
+ out.writeParcelable(intent, flags);
+ }
+
+ public static final Parcelable.Creator<LegacyVpnInfo> CREATOR =
+ new Parcelable.Creator<LegacyVpnInfo>() {
+ @Override
+ public LegacyVpnInfo createFromParcel(Parcel in) {
+ LegacyVpnInfo info = new LegacyVpnInfo();
+ info.key = in.readString();
+ info.state = in.readInt();
+ info.intent = in.readParcelable(null);
+ return info;
+ }
+
+ @Override
+ public LegacyVpnInfo[] newArray(int size) {
+ return new LegacyVpnInfo[size];
+ }
+ };
+}
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index 572a1d7..d36be10 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -21,7 +21,6 @@
import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.SystemClock;
import java.util.List;
@@ -35,31 +34,33 @@
public static final String ACTION_VPN_REVOKED = "android.net.vpn.action.REVOKED";
+ public static final String LEGACY_VPN = "[Legacy VPN]";
+
public static Intent getIntentForConfirmation() {
Intent intent = new Intent();
intent.setClassName("com.android.vpndialogs", "com.android.vpndialogs.ConfirmDialog");
return intent;
}
- public static PendingIntent getIntentForNotification(Context context, VpnConfig config) {
- config.startTime = SystemClock.elapsedRealtime();
+ public static PendingIntent getIntentForStatusPanel(Context context, VpnConfig config) {
Intent intent = new Intent();
intent.setClassName("com.android.vpndialogs", "com.android.vpndialogs.ManageDialog");
intent.putExtra("config", config);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY |
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+ return PendingIntent.getActivity(context, 0, intent, (config == null) ?
+ PendingIntent.FLAG_NO_CREATE : PendingIntent.FLAG_CANCEL_CURRENT);
}
- public String packageName;
- public String sessionName;
- public String interfaceName;
- public PendingIntent configureIntent;
+ public String packagz;
+ public String interfaze;
+ public String session;
public int mtu = -1;
public String addresses;
public String routes;
public List<String> dnsServers;
public List<String> searchDomains;
+ public PendingIntent configureIntent;
public long startTime = -1;
@Override
@@ -69,15 +70,15 @@
@Override
public void writeToParcel(Parcel out, int flags) {
- out.writeString(packageName);
- out.writeString(sessionName);
- out.writeString(interfaceName);
- out.writeParcelable(configureIntent, flags);
+ out.writeString(packagz);
+ out.writeString(interfaze);
+ out.writeString(session);
out.writeInt(mtu);
out.writeString(addresses);
out.writeString(routes);
out.writeStringList(dnsServers);
out.writeStringList(searchDomains);
+ out.writeParcelable(configureIntent, flags);
out.writeLong(startTime);
}
@@ -86,15 +87,15 @@
@Override
public VpnConfig createFromParcel(Parcel in) {
VpnConfig config = new VpnConfig();
- config.packageName = in.readString();
- config.sessionName = in.readString();
- config.interfaceName = in.readString();
- config.configureIntent = in.readParcelable(null);
+ config.packagz = in.readString();
+ config.interfaze = in.readString();
+ config.session = in.readString();
config.mtu = in.readInt();
config.addresses = in.readString();
config.routes = in.readString();
config.dnsServers = in.createStringArrayList();
config.searchDomains = in.createStringArrayList();
+ config.configureIntent = in.readParcelable(null);
config.startTime = in.readLong();
return config;
}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItem.java b/core/java/com/android/internal/view/menu/ActionMenuItem.java
index a4bcf60..2685046 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuItem.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuItem.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
+import android.view.ActionProvider;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.MenuItem;
import android.view.SubMenu;
@@ -238,6 +239,16 @@
}
@Override
+ public ActionProvider getActionProvider() {
+ return null;
+ }
+
+ @Override
+ public MenuItem setActionProvider(ActionProvider actionProvider) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public MenuItem setShowAsActionFlags(int actionEnum) {
setShowAsAction(actionEnum);
return this;
diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java
index 253511c..7b1dfb0 100644
--- a/core/java/com/android/internal/view/menu/MenuItemImpl.java
+++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java
@@ -23,6 +23,7 @@
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.util.Log;
+import android.view.ActionProvider;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.MenuItem;
@@ -79,6 +80,7 @@
private int mShowAsAction = SHOW_AS_ACTION_NEVER;
private View mActionView;
+ private ActionProvider mActionProvider;
private OnActionExpandListener mOnActionExpandListener;
private boolean mIsActionViewExpanded = false;
@@ -98,10 +100,8 @@
/**
- * Instantiates this menu item. The constructor
- * {@link #MenuItemData(MenuBuilder, int, int, int, CharSequence, int)} is
- * preferred due to lazy loading of the icon Drawable.
- *
+ * Instantiates this menu item.
+ *
* @param menu
* @param group Item ordering grouping control. The item will be added after
* all other items whose order is <= this number, and before any
@@ -154,7 +154,7 @@
mItemCallback.run();
return true;
}
-
+
if (mIntent != null) {
try {
mMenu.getContext().startActivity(mIntent);
@@ -163,7 +163,14 @@
Log.e(TAG, "Can't find activity to handle intent; ignoring", e);
}
}
-
+
+ if (mActionProvider != null) {
+ // The action view is created by the provider in this case.
+ View actionView = getActionView();
+ mActionProvider.onPerformDefaultAction(actionView);
+ return true;
+ }
+
return false;
}
@@ -551,6 +558,7 @@
public MenuItem setActionView(View view) {
mActionView = view;
+ mActionProvider = null;
mMenu.onItemActionRequestChanged(this);
return this;
}
@@ -563,7 +571,25 @@
}
public View getActionView() {
- return mActionView;
+ if (mActionView != null) {
+ return mActionView;
+ } else if (mActionProvider != null) {
+ mActionView = mActionProvider.onCreateActionView();
+ return mActionView;
+ } else {
+ return null;
+ }
+ }
+
+ public ActionProvider getActionProvider() {
+ return mActionProvider;
+ }
+
+ public MenuItem setActionProvider(ActionProvider actionProvider) {
+ mActionView = null;
+ mActionProvider = actionProvider;
+ mMenu.onItemsChanged(false);
+ return this;
}
@Override
diff --git a/core/res/res/drawable-hdpi/ic_ab_back_holo_dark.png b/core/res/res/drawable-hdpi/ic_ab_back_holo_dark.png
index 7855cda..897a1c1 100644
--- a/core/res/res/drawable-hdpi/ic_ab_back_holo_dark.png
+++ b/core/res/res/drawable-hdpi/ic_ab_back_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_ab_back_holo_light.png b/core/res/res/drawable-hdpi/ic_ab_back_holo_light.png
index c062773..0c89f71 100644
--- a/core/res/res/drawable-hdpi/ic_ab_back_holo_light.png
+++ b/core/res/res/drawable-hdpi/ic_ab_back_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_copy.png b/core/res/res/drawable-hdpi/ic_menu_copy.png
new file mode 100644
index 0000000..5dcc3a3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_copy.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_copy_holo_dark.png b/core/res/res/drawable-hdpi/ic_menu_copy_holo_dark.png
index 852f146..d1e1337 100644
--- a/core/res/res/drawable-hdpi/ic_menu_copy_holo_dark.png
+++ b/core/res/res/drawable-hdpi/ic_menu_copy_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_copy_holo_light.png b/core/res/res/drawable-hdpi/ic_menu_copy_holo_light.png
index ad09b37..5d02660 100644
--- a/core/res/res/drawable-hdpi/ic_menu_copy_holo_light.png
+++ b/core/res/res/drawable-hdpi/ic_menu_copy_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_cut.png b/core/res/res/drawable-hdpi/ic_menu_cut.png
new file mode 100644
index 0000000..03fac98
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_cut.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_cut_holo_dark.png b/core/res/res/drawable-hdpi/ic_menu_cut_holo_dark.png
index 7716a94..bd28a859 100644
--- a/core/res/res/drawable-hdpi/ic_menu_cut_holo_dark.png
+++ b/core/res/res/drawable-hdpi/ic_menu_cut_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_cut_holo_light.png b/core/res/res/drawable-hdpi/ic_menu_cut_holo_light.png
index bea6db1..037c362 100644
--- a/core/res/res/drawable-hdpi/ic_menu_cut_holo_light.png
+++ b/core/res/res/drawable-hdpi/ic_menu_cut_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_find_holo_dark.png b/core/res/res/drawable-hdpi/ic_menu_find_holo_dark.png
index b888202..b981a4d 100644
--- a/core/res/res/drawable-hdpi/ic_menu_find_holo_dark.png
+++ b/core/res/res/drawable-hdpi/ic_menu_find_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_find_holo_light.png b/core/res/res/drawable-hdpi/ic_menu_find_holo_light.png
index b888202..efee6df 100644
--- a/core/res/res/drawable-hdpi/ic_menu_find_holo_light.png
+++ b/core/res/res/drawable-hdpi/ic_menu_find_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_moreoverflow.png b/core/res/res/drawable-hdpi/ic_menu_moreoverflow.png
new file mode 100644
index 0000000..33bb5e76
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_moreoverflow.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_dark.png b/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_dark.png
index 8563c1a8d..1e69eac 100644
--- a/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_dark.png
+++ b/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_light.png b/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_light.png
index 1cd2384..2f6accc 100644
--- a/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_light.png
+++ b/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_paste.png b/core/res/res/drawable-hdpi/ic_menu_paste.png
new file mode 100644
index 0000000..c24fd86
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_paste.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_paste_holo_dark.png b/core/res/res/drawable-hdpi/ic_menu_paste_holo_dark.png
index 5579443..e9514b8 100644
--- a/core/res/res/drawable-hdpi/ic_menu_paste_holo_dark.png
+++ b/core/res/res/drawable-hdpi/ic_menu_paste_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_paste_holo_light.png b/core/res/res/drawable-hdpi/ic_menu_paste_holo_light.png
index 6674914..b02aa09 100644
--- a/core/res/res/drawable-hdpi/ic_menu_paste_holo_light.png
+++ b/core/res/res/drawable-hdpi/ic_menu_paste_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_share_holo_dark.png b/core/res/res/drawable-hdpi/ic_menu_share_holo_dark.png
index 2837615..db011be 100644
--- a/core/res/res/drawable-hdpi/ic_menu_share_holo_dark.png
+++ b/core/res/res/drawable-hdpi/ic_menu_share_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_share_holo_light.png b/core/res/res/drawable-hdpi/ic_menu_share_holo_light.png
index 2837615..d9a9a73 100644
--- a/core/res/res/drawable-hdpi/ic_menu_share_holo_light.png
+++ b/core/res/res/drawable-hdpi/ic_menu_share_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_ab_back_holo_dark.png b/core/res/res/drawable-mdpi/ic_ab_back_holo_dark.png
index ae3e6bf..df2d3d1 100644
--- a/core/res/res/drawable-mdpi/ic_ab_back_holo_dark.png
+++ b/core/res/res/drawable-mdpi/ic_ab_back_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_ab_back_holo_light.png b/core/res/res/drawable-mdpi/ic_ab_back_holo_light.png
index c61e3fa..b2aa9c2 100644
--- a/core/res/res/drawable-mdpi/ic_ab_back_holo_light.png
+++ b/core/res/res/drawable-mdpi/ic_ab_back_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_copy.png b/core/res/res/drawable-mdpi/ic_menu_copy.png
new file mode 100644
index 0000000..eee5540
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_copy.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_copy_holo_dark.png b/core/res/res/drawable-mdpi/ic_menu_copy_holo_dark.png
index 35c3318..cb19fea 100644
--- a/core/res/res/drawable-mdpi/ic_menu_copy_holo_dark.png
+++ b/core/res/res/drawable-mdpi/ic_menu_copy_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_copy_holo_light.png b/core/res/res/drawable-mdpi/ic_menu_copy_holo_light.png
index 3b179d8..e353d46 100644
--- a/core/res/res/drawable-mdpi/ic_menu_copy_holo_light.png
+++ b/core/res/res/drawable-mdpi/ic_menu_copy_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_cut.png b/core/res/res/drawable-mdpi/ic_menu_cut.png
new file mode 100644
index 0000000..865d1e0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_cut.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_cut_holo_dark.png b/core/res/res/drawable-mdpi/ic_menu_cut_holo_dark.png
index dfe8b4a..66a750d 100644
--- a/core/res/res/drawable-mdpi/ic_menu_cut_holo_dark.png
+++ b/core/res/res/drawable-mdpi/ic_menu_cut_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_cut_holo_light.png b/core/res/res/drawable-mdpi/ic_menu_cut_holo_light.png
index 748dc9b..e7e8c54 100644
--- a/core/res/res/drawable-mdpi/ic_menu_cut_holo_light.png
+++ b/core/res/res/drawable-mdpi/ic_menu_cut_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_find_holo_dark.png b/core/res/res/drawable-mdpi/ic_menu_find_holo_dark.png
index 82dcba7..45f8fd3 100644
--- a/core/res/res/drawable-mdpi/ic_menu_find_holo_dark.png
+++ b/core/res/res/drawable-mdpi/ic_menu_find_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_find_holo_light.png b/core/res/res/drawable-mdpi/ic_menu_find_holo_light.png
index 82dcba7..9033f1e 100644
--- a/core/res/res/drawable-mdpi/ic_menu_find_holo_light.png
+++ b/core/res/res/drawable-mdpi/ic_menu_find_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_moreoverflow.png b/core/res/res/drawable-mdpi/ic_menu_moreoverflow.png
new file mode 100644
index 0000000..e478922
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_moreoverflow.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_moreoverflow_focused_holo_dark.png b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_focused_holo_dark.png
index c369e6f..48d6c78 100644
--- a/core/res/res/drawable-mdpi/ic_menu_moreoverflow_focused_holo_dark.png
+++ b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_moreoverflow_focused_holo_light.png b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_focused_holo_light.png
index a4df2bf..50ff8fc 100644
--- a/core/res/res/drawable-mdpi/ic_menu_moreoverflow_focused_holo_light.png
+++ b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_dark.png b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_dark.png
index a7389c9..135ca6e 100644
--- a/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_dark.png
+++ b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_light.png b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_light.png
index 87e41ac..ccbf143 100644
--- a/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_light.png
+++ b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_paste.png b/core/res/res/drawable-mdpi/ic_menu_paste.png
new file mode 100644
index 0000000..8c9916c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_menu_paste.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_paste_holo_dark.png b/core/res/res/drawable-mdpi/ic_menu_paste_holo_dark.png
index caec299..23f3a32 100644
--- a/core/res/res/drawable-mdpi/ic_menu_paste_holo_dark.png
+++ b/core/res/res/drawable-mdpi/ic_menu_paste_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_paste_holo_light.png b/core/res/res/drawable-mdpi/ic_menu_paste_holo_light.png
index 434f5d1..c9d571c 100644
--- a/core/res/res/drawable-mdpi/ic_menu_paste_holo_light.png
+++ b/core/res/res/drawable-mdpi/ic_menu_paste_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_share_holo_dark.png b/core/res/res/drawable-mdpi/ic_menu_share_holo_dark.png
old mode 100755
new mode 100644
index d89ca5f..306cac8
--- a/core/res/res/drawable-mdpi/ic_menu_share_holo_dark.png
+++ b/core/res/res/drawable-mdpi/ic_menu_share_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_share_holo_light.png b/core/res/res/drawable-mdpi/ic_menu_share_holo_light.png
old mode 100755
new mode 100644
index d89ca5f..cc081ad
--- a/core/res/res/drawable-mdpi/ic_menu_share_holo_light.png
+++ b/core/res/res/drawable-mdpi/ic_menu_share_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_ab_back_holo_dark.png b/core/res/res/drawable-xhdpi/ic_ab_back_holo_dark.png
new file mode 100644
index 0000000..8ded62f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_ab_back_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_ab_back_holo_light.png b/core/res/res/drawable-xhdpi/ic_ab_back_holo_light.png
new file mode 100644
index 0000000..517e9f7
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_ab_back_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_account_list.png b/core/res/res/drawable-xhdpi/ic_menu_account_list.png
new file mode 100644
index 0000000..ebe29b96
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_account_list.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_add.png b/core/res/res/drawable-xhdpi/ic_menu_add.png
new file mode 100644
index 0000000..7d498a9
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_add.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_agenda.png b/core/res/res/drawable-xhdpi/ic_menu_agenda.png
new file mode 100644
index 0000000..25e9f11
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_agenda.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_allfriends.png b/core/res/res/drawable-xhdpi/ic_menu_allfriends.png
new file mode 100644
index 0000000..20994ed
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_allfriends.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_always_landscape_portrait.png b/core/res/res/drawable-xhdpi/ic_menu_always_landscape_portrait.png
new file mode 100644
index 0000000..96606de
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_always_landscape_portrait.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_archive.png b/core/res/res/drawable-xhdpi/ic_menu_archive.png
new file mode 100644
index 0000000..b1be9d5
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_archive.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_attachment.png b/core/res/res/drawable-xhdpi/ic_menu_attachment.png
new file mode 100644
index 0000000..aa41bd6
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_attachment.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_back.png b/core/res/res/drawable-xhdpi/ic_menu_back.png
new file mode 100644
index 0000000..8ac4f64
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_back.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_block.png b/core/res/res/drawable-xhdpi/ic_menu_block.png
new file mode 100644
index 0000000..e672395
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_block.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_blocked_user.png b/core/res/res/drawable-xhdpi/ic_menu_blocked_user.png
new file mode 100644
index 0000000..53a279e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_blocked_user.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_btn_add.png b/core/res/res/drawable-xhdpi/ic_menu_btn_add.png
new file mode 100644
index 0000000..7d498a9
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_btn_add.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_call.png b/core/res/res/drawable-xhdpi/ic_menu_call.png
new file mode 100644
index 0000000..703a76b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_call.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_camera.png b/core/res/res/drawable-xhdpi/ic_menu_camera.png
new file mode 100644
index 0000000..7875aa3
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_camera.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_cc.png b/core/res/res/drawable-xhdpi/ic_menu_cc.png
new file mode 100644
index 0000000..50d686a
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_cc.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_chat_dashboard.png b/core/res/res/drawable-xhdpi/ic_menu_chat_dashboard.png
new file mode 100644
index 0000000..c0b238c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_chat_dashboard.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_clear_playlist.png b/core/res/res/drawable-xhdpi/ic_menu_clear_playlist.png
new file mode 100644
index 0000000..8981d6f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_clear_playlist.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_close_clear_cancel.png b/core/res/res/drawable-xhdpi/ic_menu_close_clear_cancel.png
new file mode 100644
index 0000000..d743d75
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_close_clear_cancel.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_compass.png b/core/res/res/drawable-xhdpi/ic_menu_compass.png
new file mode 100644
index 0000000..1c2ad89
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_compass.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_compose.png b/core/res/res/drawable-xhdpi/ic_menu_compose.png
new file mode 100644
index 0000000..bef190e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_compose.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_copy.png b/core/res/res/drawable-xhdpi/ic_menu_copy.png
new file mode 100644
index 0000000..22761fc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_copy.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_copy_holo_dark.png b/core/res/res/drawable-xhdpi/ic_menu_copy_holo_dark.png
new file mode 100644
index 0000000..8014345
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_copy_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_copy_holo_light.png b/core/res/res/drawable-xhdpi/ic_menu_copy_holo_light.png
new file mode 100644
index 0000000..b5359a1
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_copy_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_crop.png b/core/res/res/drawable-xhdpi/ic_menu_crop.png
new file mode 100644
index 0000000..d32daae
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_crop.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_cut.png b/core/res/res/drawable-xhdpi/ic_menu_cut.png
new file mode 100644
index 0000000..efefcde
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_cut.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_cut_holo_dark.png b/core/res/res/drawable-xhdpi/ic_menu_cut_holo_dark.png
new file mode 100644
index 0000000..180365f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_cut_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_cut_holo_light.png b/core/res/res/drawable-xhdpi/ic_menu_cut_holo_light.png
new file mode 100644
index 0000000..a31a06f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_cut_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_day.png b/core/res/res/drawable-xhdpi/ic_menu_day.png
new file mode 100644
index 0000000..9eed1b2
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_day.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_delete.png b/core/res/res/drawable-xhdpi/ic_menu_delete.png
new file mode 100644
index 0000000..65b9cae
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_delete.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_directions.png b/core/res/res/drawable-xhdpi/ic_menu_directions.png
new file mode 100644
index 0000000..bdc0088
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_directions.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_edit.png b/core/res/res/drawable-xhdpi/ic_menu_edit.png
new file mode 100644
index 0000000..fcdd71e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_edit.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_emoticons.png b/core/res/res/drawable-xhdpi/ic_menu_emoticons.png
new file mode 100644
index 0000000..af730fa
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_emoticons.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_end_conversation.png b/core/res/res/drawable-xhdpi/ic_menu_end_conversation.png
new file mode 100644
index 0000000..ac76f3b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_end_conversation.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_find.png b/core/res/res/drawable-xhdpi/ic_menu_find.png
new file mode 100644
index 0000000..ccf2aab
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_find.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_find_holo_dark.png b/core/res/res/drawable-xhdpi/ic_menu_find_holo_dark.png
new file mode 100644
index 0000000..3ede9e2
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_find_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_find_holo_light.png b/core/res/res/drawable-xhdpi/ic_menu_find_holo_light.png
new file mode 100644
index 0000000..de20fa0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_find_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_forward.png b/core/res/res/drawable-xhdpi/ic_menu_forward.png
new file mode 100644
index 0000000..6463e7a
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_forward.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_friendslist.png b/core/res/res/drawable-xhdpi/ic_menu_friendslist.png
new file mode 100644
index 0000000..9200f87
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_friendslist.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_gallery.png b/core/res/res/drawable-xhdpi/ic_menu_gallery.png
new file mode 100644
index 0000000..6b21e22
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_gallery.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_goto.png b/core/res/res/drawable-xhdpi/ic_menu_goto.png
new file mode 100644
index 0000000..b925e69
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_goto.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_help.png b/core/res/res/drawable-xhdpi/ic_menu_help.png
new file mode 100644
index 0000000..128c7e8
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_help.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_help_holo_light.png b/core/res/res/drawable-xhdpi/ic_menu_help_holo_light.png
new file mode 100644
index 0000000..b961de9
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_help_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_home.png b/core/res/res/drawable-xhdpi/ic_menu_home.png
new file mode 100644
index 0000000..689f372
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_home.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_info_details.png b/core/res/res/drawable-xhdpi/ic_menu_info_details.png
new file mode 100644
index 0000000..24ea543
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_info_details.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_invite.png b/core/res/res/drawable-xhdpi/ic_menu_invite.png
new file mode 100644
index 0000000..d594607
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_invite.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_login.png b/core/res/res/drawable-xhdpi/ic_menu_login.png
new file mode 100644
index 0000000..5095ed9
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_login.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_manage.png b/core/res/res/drawable-xhdpi/ic_menu_manage.png
new file mode 100644
index 0000000..d7436244
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_manage.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_mapmode.png b/core/res/res/drawable-xhdpi/ic_menu_mapmode.png
new file mode 100644
index 0000000..0b62d08
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_mapmode.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_mark.png b/core/res/res/drawable-xhdpi/ic_menu_mark.png
new file mode 100644
index 0000000..a5de6fb
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_mark.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_month.png b/core/res/res/drawable-xhdpi/ic_menu_month.png
new file mode 100644
index 0000000..099263b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_month.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_more.png b/core/res/res/drawable-xhdpi/ic_menu_more.png
new file mode 100644
index 0000000..c7a6538
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_more.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_moreoverflow.png b/core/res/res/drawable-xhdpi/ic_menu_moreoverflow.png
new file mode 100644
index 0000000..2998d65
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_moreoverflow.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_focused_holo_dark.png b/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_focused_holo_dark.png
new file mode 100644
index 0000000..62659fa
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_focused_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_focused_holo_light.png b/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_focused_holo_light.png
new file mode 100644
index 0000000..341edaf
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_focused_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_dark.png b/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_dark.png
new file mode 100644
index 0000000..81306ca
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_light.png b/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_light.png
new file mode 100644
index 0000000..1f46e7a
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_my_calendar.png b/core/res/res/drawable-xhdpi/ic_menu_my_calendar.png
new file mode 100644
index 0000000..ca95b92
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_my_calendar.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_mylocation.png b/core/res/res/drawable-xhdpi/ic_menu_mylocation.png
new file mode 100644
index 0000000..b0a76a2
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_mylocation.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_myplaces.png b/core/res/res/drawable-xhdpi/ic_menu_myplaces.png
new file mode 100644
index 0000000..205848e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_myplaces.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_notifications.png b/core/res/res/drawable-xhdpi/ic_menu_notifications.png
new file mode 100644
index 0000000..db80b57
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_notifications.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_paste.png b/core/res/res/drawable-xhdpi/ic_menu_paste.png
new file mode 100644
index 0000000..a69f0eb
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_paste.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_paste_holo_dark.png b/core/res/res/drawable-xhdpi/ic_menu_paste_holo_dark.png
new file mode 100644
index 0000000..6e7273f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_paste_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_paste_holo_light.png b/core/res/res/drawable-xhdpi/ic_menu_paste_holo_light.png
new file mode 100644
index 0000000..b7eedd9
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_paste_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_play_clip.png b/core/res/res/drawable-xhdpi/ic_menu_play_clip.png
new file mode 100644
index 0000000..f680fce
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_play_clip.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_preferences.png b/core/res/res/drawable-xhdpi/ic_menu_preferences.png
new file mode 100644
index 0000000..02cfbad
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_preferences.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_recent_history.png b/core/res/res/drawable-xhdpi/ic_menu_recent_history.png
new file mode 100644
index 0000000..fc5e1fc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_recent_history.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_refresh.png b/core/res/res/drawable-xhdpi/ic_menu_refresh.png
new file mode 100644
index 0000000..9e9f10e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_refresh.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_report_image.png b/core/res/res/drawable-xhdpi/ic_menu_report_image.png
new file mode 100644
index 0000000..26f7ff4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_report_image.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_revert.png b/core/res/res/drawable-xhdpi/ic_menu_revert.png
new file mode 100644
index 0000000..19c580f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_revert.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_rotate.png b/core/res/res/drawable-xhdpi/ic_menu_rotate.png
new file mode 100644
index 0000000..98e19fe
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_rotate.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_save.png b/core/res/res/drawable-xhdpi/ic_menu_save.png
new file mode 100644
index 0000000..62a66d8
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_save.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_search.png b/core/res/res/drawable-xhdpi/ic_menu_search.png
new file mode 100644
index 0000000..5c18f9e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_search.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_search_holo_dark.png b/core/res/res/drawable-xhdpi/ic_menu_search_holo_dark.png
new file mode 100644
index 0000000..d49c7e2
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_search_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_search_holo_light.png b/core/res/res/drawable-xhdpi/ic_menu_search_holo_light.png
new file mode 100644
index 0000000..578cb24
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_search_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_selectall_holo_dark.png b/core/res/res/drawable-xhdpi/ic_menu_selectall_holo_dark.png
new file mode 100644
index 0000000..7125557
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_selectall_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_selectall_holo_light.png b/core/res/res/drawable-xhdpi/ic_menu_selectall_holo_light.png
new file mode 100644
index 0000000..c7728d4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_selectall_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_send.png b/core/res/res/drawable-xhdpi/ic_menu_send.png
new file mode 100644
index 0000000..6e5ec78
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_send.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_set_as.png b/core/res/res/drawable-xhdpi/ic_menu_set_as.png
new file mode 100644
index 0000000..8689766
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_set_as.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_settings_holo_light.png b/core/res/res/drawable-xhdpi/ic_menu_settings_holo_light.png
new file mode 100644
index 0000000..aa33c38
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_settings_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_share.png b/core/res/res/drawable-xhdpi/ic_menu_share.png
new file mode 100644
index 0000000..fce1d35
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_share.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_share_holo_dark.png b/core/res/res/drawable-xhdpi/ic_menu_share_holo_dark.png
new file mode 100644
index 0000000..af72732
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_share_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_share_holo_light.png b/core/res/res/drawable-xhdpi/ic_menu_share_holo_light.png
new file mode 100644
index 0000000..79c162f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_share_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_slideshow.png b/core/res/res/drawable-xhdpi/ic_menu_slideshow.png
new file mode 100644
index 0000000..8740f37
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_slideshow.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_sort_alphabetically.png b/core/res/res/drawable-xhdpi/ic_menu_sort_alphabetically.png
new file mode 100644
index 0000000..5736ff8
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_sort_alphabetically.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_sort_by_size.png b/core/res/res/drawable-xhdpi/ic_menu_sort_by_size.png
new file mode 100644
index 0000000..fe3836c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_sort_by_size.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_star.png b/core/res/res/drawable-xhdpi/ic_menu_star.png
new file mode 100644
index 0000000..c051020
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_star.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_start_conversation.png b/core/res/res/drawable-xhdpi/ic_menu_start_conversation.png
new file mode 100644
index 0000000..d71ed174
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_start_conversation.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_stop.png b/core/res/res/drawable-xhdpi/ic_menu_stop.png
new file mode 100644
index 0000000..855af11
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_stop.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_today.png b/core/res/res/drawable-xhdpi/ic_menu_today.png
new file mode 100644
index 0000000..e9ebc5e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_today.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_upload.png b/core/res/res/drawable-xhdpi/ic_menu_upload.png
new file mode 100644
index 0000000..94d1478
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_upload.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_upload_you_tube.png b/core/res/res/drawable-xhdpi/ic_menu_upload_you_tube.png
new file mode 100644
index 0000000..508c354
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_upload_you_tube.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_view.png b/core/res/res/drawable-xhdpi/ic_menu_view.png
new file mode 100644
index 0000000..e97c30df
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_view.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_week.png b/core/res/res/drawable-xhdpi/ic_menu_week.png
new file mode 100644
index 0000000..2c3e761
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_week.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_zoom.png b/core/res/res/drawable-xhdpi/ic_menu_zoom.png
new file mode 100644
index 0000000..858aef5
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_menu_zoom.png
Binary files differ
diff --git a/core/res/res/layout-land/ssl_certificate.xml b/core/res/res/layout-land/ssl_certificate.xml
index c3e6deb..4f8bd8e 100644
--- a/core/res/res/layout-land/ssl_certificate.xml
+++ b/core/res/res/layout-land/ssl_certificate.xml
@@ -23,6 +23,7 @@
android:id="@+id/body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:gravity="left"
android:orientation="vertical" >
<!-- Placeholder for the success message or one or more warnings -->
@@ -43,8 +44,7 @@
android:layout_weight="1"
android:gravity="fill_horizontal"
android:layout_marginLeft="20dip"
- android:layout_marginRight="20dip"
- android:layout_marginBottom="12dip" />
+ android:layout_marginRight="20dip"/>
<TableLayout
android:layout_width="match_parent"
@@ -58,7 +58,8 @@
android:text="@string/issued_to"
android:textStyle="bold"
android:layout_height="wrap_content"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginTop="12dip"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:layout_marginBottom="7dip" />
@@ -68,12 +69,10 @@
<TextView
android:id="@+id/to_common_header"
android:text="@string/common_name"
- android:gravity="left"
android:layout_marginLeft="20dip" />
<TextView
android:id="@+id/to_common"
- android:gravity="left"
android:layout_marginLeft="10dip"
android:layout_marginRight="20dip"
android:layout_marginBottom="7dip" />
@@ -84,12 +83,10 @@
<TextView
android:id="@+id/to_org_header"
android:text="@string/org_name"
- android:gravity="left"
android:layout_marginLeft="20dip" />
<TextView
android:id="@+id/to_org"
- android:gravity="left"
android:layout_marginLeft="10dip"
android:layout_marginRight="20dip"
android:layout_marginBottom="7dip" />
@@ -100,15 +97,27 @@
<TextView
android:id="@+id/to_org_unit_header"
android:text="@string/org_unit"
- android:gravity="left"
android:layout_marginLeft="20dip" />
<TextView
android:id="@+id/to_org_unit"
- android:gravity="left"
android:layout_marginLeft="10dip"
android:layout_marginRight="20dip"
- android:layout_marginBottom="12dip" />
+ android:layout_marginBottom="7dip" />
+ </TableRow>
+
+ <!-- Serial number: -->
+ <TableRow>
+ <TextView
+ android:id="@+id/serial_number_header"
+ android:text="@string/serial_number"
+ android:layout_marginLeft="20dip" />
+
+ <TextView
+ android:id="@+id/serial_number"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
</TableRow>
<!-- Issued by: -->
@@ -117,7 +126,8 @@
android:text="@string/issued_by"
android:textStyle="bold"
android:layout_height="wrap_content"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginTop="12dip"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:layout_marginBottom="7dip" />
@@ -127,12 +137,10 @@
<TextView
android:id="@+id/by_common_header"
android:text="@string/common_name"
- android:gravity="left"
android:layout_marginLeft="20dip" />
<TextView
android:id="@+id/by_common"
- android:gravity="left"
android:layout_marginLeft="10dip"
android:layout_marginRight="20dip"
android:layout_marginBottom="7dip" />
@@ -143,12 +151,10 @@
<TextView
android:id="@+id/by_org_header"
android:text="@string/org_name"
- android:gravity="left"
android:layout_marginLeft="20dip" />
<TextView
android:id="@+id/by_org"
- android:gravity="left"
android:layout_marginLeft="10dip"
android:layout_marginRight="20dip"
android:layout_marginBottom="7dip" />
@@ -159,24 +165,23 @@
<TextView
android:id="@+id/by_org_unit_header"
android:text="@string/org_unit"
- android:gravity="left"
android:layout_marginLeft="20dip" />
<TextView
android:id="@+id/by_org_unit"
- android:gravity="left"
android:layout_marginLeft="10dip"
android:layout_marginRight="20dip"
- android:layout_marginBottom="12dip" />
+ android:layout_marginBottom="7dip" />
</TableRow>
<!-- Validity Dates: -->
<TextView
android:id="@+id/validity_header"
android:layout_height="wrap_content"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:text="@string/validity_period"
android:textStyle="bold"
+ android:layout_marginTop="12dip"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:layout_marginBottom="7dip" />
@@ -185,17 +190,15 @@
<TableRow>
<TextView
android:id="@+id/issued_on_header"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/issued_on"
- android:gravity="left"
android:layout_marginLeft="20dip" />
<TextView
android:id="@+id/issued_on"
android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:gravity="left"
+ android:layout_width="match_parent"
android:layout_marginLeft="10dip"
android:layout_marginRight="20dip"
android:layout_marginBottom="7dip" />
@@ -206,20 +209,68 @@
<TableRow>
<TextView
android:id="@+id/expires_on_header"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/expires_on"
- android:gravity="left"
android:layout_marginLeft="20dip" />
<TextView
android:id="@+id/expires_on"
android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:gravity="left"
+ android:layout_width="match_parent"
android:layout_marginLeft="10dip"
android:layout_marginRight="20dip"
- android:layout_marginBottom="12dip" />
+ android:layout_marginBottom="7dip" />
+ </TableRow>
+
+ <!-- Fingerprints: -->
+ <TextView
+ android:id="@+id/fingerprints"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:text="@string/fingerprints"
+ android:textStyle="bold"
+ android:layout_marginTop="12dip"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
+
+ <!-- SHA-256 fingerprint: -->
+ <TableRow>
+ <TextView
+ android:id="@+id/sha256_fingerprint_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/sha256_fingerprint"
+ android:layout_marginLeft="20dip" />
+
+ <TextView
+ android:id="@+id/sha256_fingerprint"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
+
+ </TableRow>
+
+ <!-- SHA-1 fingerprint: -->
+ <TableRow>
+ <TextView
+ android:id="@+id/sha1_fingerprint_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/sha1_fingerprint"
+ android:layout_marginLeft="20dip" />
+
+ <TextView
+ android:id="@+id/sha1_fingerprint"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
+
</TableRow>
</TableLayout>
diff --git a/core/res/res/layout/activity_chooser_list_footer.xml b/core/res/res/layout/activity_chooser_list_footer.xml
new file mode 100644
index 0000000..7603a31
--- /dev/null
+++ b/core/res/res/layout/activity_chooser_list_footer.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/list_footer"
+ android:paddingLeft="16dip"
+ android:paddingRight="16dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/divider"
+ android:layout_width="match_parent"
+ android:layout_height="2dip"
+ android:scaleType="fitXY"
+ android:gravity="fill_horizontal"
+ android:src="@drawable/divider_strong_holo" />
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="48dip"
+ android:gravity="center"
+ android:textAppearance="?android:attr/textAppearanceLargePopupMenu"
+ android:duplicateParentState="true"
+ android:singleLine="true"
+ android:text="@string/activity_chooser_view_see_all" />
+
+</LinearLayout>
diff --git a/core/res/res/layout/activity_chooser_list_header.xml b/core/res/res/layout/activity_chooser_list_header.xml
new file mode 100644
index 0000000..867014b9
--- /dev/null
+++ b/core/res/res/layout/activity_chooser_list_header.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/list_header"
+ android:paddingLeft="16dip"
+ android:paddingRight="16dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="?android:attr/dropdownListPreferredItemHeight"
+ android:gravity="center"
+ android:textAppearance="?android:attr/textAppearanceLargePopupMenu"
+ android:duplicateParentState="true"
+ android:singleLine="true" />
+
+ <ImageView
+ android:id="@+id/divider"
+ android:layout_width="match_parent"
+ android:layout_height="2dip"
+ android:scaleType="fitXY"
+ android:gravity="fill_horizontal"
+ android:src="@drawable/divider_strong_holo" />
+
+</LinearLayout>
diff --git a/core/res/res/layout/activity_chooser_view.xml b/core/res/res/layout/activity_chooser_view.xml
new file mode 100644
index 0000000..ccf49fc
--- /dev/null
+++ b/core/res/res/layout/activity_chooser_view.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/activity_chooser_view_content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="?android:attr/actionButtonStyle">
+
+ <ImageButton android:id="@+id/default_activity_button"
+ android:layout_width="32dip"
+ android:layout_height="32dip"
+ android:layout_marginLeft="16dip" />
+
+ <ImageButton android:id="@+id/expand_activities_button"
+ android:layout_width="32dip"
+ android:layout_height="32dip"
+ android:layout_marginLeft="16dip" />
+
+</LinearLayout>
diff --git a/core/res/res/layout/activity_chooser_view_list_item.xml b/core/res/res/layout/activity_chooser_view_list_item.xml
new file mode 100644
index 0000000..61b7e70
--- /dev/null
+++ b/core/res/res/layout/activity_chooser_view_list_item.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/list_item"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/dropdownListPreferredItemHeight"
+ android:gravity="center_vertical"
+ android:paddingLeft="16dip"
+ android:paddingRight="16dip">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="32dip"
+ android:layout_height="32dip"
+ android:layout_gravity="center_vertical"
+ android:layout_marginRight="8dip"
+ android:duplicateParentState="true" />
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLargePopupMenu"
+ android:singleLine="true"
+ android:duplicateParentState="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal" />
+
+</LinearLayout>
diff --git a/core/res/res/layout/ssl_certificate.xml b/core/res/res/layout/ssl_certificate.xml
index ae661ce..dd89a2d 100644
--- a/core/res/res/layout/ssl_certificate.xml
+++ b/core/res/res/layout/ssl_certificate.xml
@@ -23,6 +23,7 @@
android:id="@+id/body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:gravity="left"
android:orientation="vertical" >
<!-- Placeholder for the success message or one or more warnings -->
@@ -43,200 +44,242 @@
android:layout_weight="1"
android:gravity="fill_horizontal"
android:layout_marginRight="20dip"
- android:layout_marginLeft="20dip"
- android:layout_marginBottom="12dip" />
+ android:layout_marginLeft="20dip"/>
- <TableLayout
+ <!-- Issued to: -->
+ <TextView
+ android:id="@+id/issued_to_header"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:text="@string/issued_to"
+ android:textStyle="bold"
+ android:layout_marginTop="12dip"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
+
+ <!-- Common name: -->
+ <TextView
+ android:id="@+id/to_common_header"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:text="@string/common_name"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip" />
+
+ <TextView
+ android:id="@+id/to_common"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
+
+ <!-- Organization: -->
+ <TextView
+ android:id="@+id/to_org_header"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:text="@string/org_name"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip" />
+
+ <TextView
+ android:id="@+id/to_org"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
+
+ <!-- Organizational unit: -->
+ <TextView
+ android:id="@+id/to_org_unit_header"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:text="@string/org_unit"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip" />
+
+ <TextView
+ android:id="@+id/to_org_unit"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
+
+ <!-- Serial number: -->
+ <TextView
+ android:id="@+id/serial_number_header"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:text="@string/serial_number"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip" />
+
+ <TextView
+ android:id="@+id/serial_number"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
+
+ <!-- Issued by: -->
+ <TextView
+ android:id="@+id/issued_to_header"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:text="@string/issued_by"
+ android:textStyle="bold"
+ android:layout_marginTop="12dip"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
+
+ <!-- Common name: -->
+ <TextView
+ android:id="@+id/by_common_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical">
+ android:text="@string/common_name"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip" />
- <!-- Issued to: -->
- <TextView
- android:id="@+id/issued_to_header"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:text="@string/issued_to"
- android:textStyle="bold"
- android:layout_marginLeft="20dip"
- android:layout_marginRight="20dip"
- android:layout_marginBottom="10dip" />
+ <TextView
+ android:id="@+id/by_common"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
- <!-- Common name: -->
- <TextView
- android:id="@+id/to_common_header"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:text="@string/common_name"
- android:gravity="left"
- android:layout_marginLeft="20dip"
- android:layout_marginRight="20dip" />
+ <!-- Organization: -->
+ <TextView
+ android:id="@+id/by_org_header"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:text="@string/org_name"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip" />
- <TextView
- android:id="@+id/to_common"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:gravity="left"
- android:layout_marginLeft="20dip"
- android:layout_marginRight="20dip"
- android:layout_marginBottom="10dip" />
+ <TextView
+ android:id="@+id/by_org"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
- <!-- Organization: -->
- <TextView
- android:id="@+id/to_org_header"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:text="@string/org_name"
- android:gravity="left"
- android:layout_marginLeft="20dip"
- android:layout_marginRight="20dip" />
+ <!-- Organizational unit: -->
+ <TextView
+ android:id="@+id/by_org_unit_header"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:text="@string/org_unit"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip" />
- <TextView
- android:id="@+id/to_org"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:gravity="left"
- android:layout_marginLeft="20dip"
- android:layout_marginRight="20dip"
- android:layout_marginBottom="10dip" />
+ <TextView
+ android:id="@+id/by_org_unit"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
- <!-- Organizational unit: -->
- <TextView
- android:id="@+id/to_org_unit_header"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:text="@string/org_unit"
- android:gravity="left"
- android:layout_marginLeft="20dip"
- android:layout_marginRight="20dip" />
+ <!-- Validity Dates: -->
+ <TextView
+ android:id="@+id/validity_header"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:text="@string/validity_period"
+ android:textStyle="bold"
+ android:layout_marginTop="12dip"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
- <TextView
- android:id="@+id/to_org_unit"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:gravity="left"
- android:layout_marginLeft="20dip"
- android:layout_marginRight="20dip"
- android:layout_marginBottom="12dip" />
+ <!-- Issued On: -->
+ <TextView
+ android:id="@+id/issued_on_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/issued_on"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip" />
- <!-- Issued by: -->
- <TextView
- android:id="@+id/issued_to_header"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:text="@string/issued_by"
- android:textStyle="bold"
- android:layout_marginLeft="20dip"
- android:layout_marginRight="20dip"
- android:layout_marginBottom="10dip" />
+ <TextView
+ android:id="@+id/issued_on"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
- <!-- Common name: -->
- <TextView
- android:id="@+id/by_common_header"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/common_name"
- android:gravity="left"
- android:layout_marginLeft="20dip"
- android:layout_marginRight="20dip" />
+ <!-- Expires On: -->
+ <TextView
+ android:id="@+id/expires_on_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/expires_on"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip" />
- <TextView
- android:id="@+id/by_common"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:gravity="left"
- android:layout_marginLeft="20dip"
- android:layout_marginRight="20dip"
- android:layout_marginBottom="10dip" />
+ <TextView
+ android:id="@+id/expires_on"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
- <!-- Organization: -->
- <TextView
- android:id="@+id/by_org_header"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:text="@string/org_name"
- android:gravity="left"
- android:layout_marginLeft="20dip"
- android:layout_marginRight="20dip" />
+ <!-- Fingerprints: -->
+ <TextView
+ android:id="@+id/fingerprints"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:text="@string/fingerprints"
+ android:textStyle="bold"
+ android:layout_marginTop="12dip"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
- <TextView
- android:id="@+id/by_org"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:gravity="left"
- android:layout_marginLeft="20dip"
- android:layout_marginRight="20dip"
- android:layout_marginBottom="10dip" />
+ <!-- SHA-256 fingerprint: -->
+ <TextView
+ android:id="@+id/sha256_fingerprint_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/sha256_fingerprint"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip" />
- <!-- Organizational unit: -->
- <TextView
- android:id="@+id/by_org_unit_header"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:text="@string/org_unit"
- android:gravity="left"
- android:layout_marginLeft="20dip"
- android:layout_marginRight="20dip" />
+ <TextView
+ android:id="@+id/sha256_fingerprint"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
- <TextView
- android:id="@+id/by_org_unit"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:gravity="left"
- android:layout_marginLeft="20dip"
- android:layout_marginRight="20dip"
- android:layout_marginBottom="12dip" />
+ <!-- SHA-1 fingerprint: -->
+ <TextView
+ android:id="@+id/sha1_fingerprint_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/sha1_fingerprint"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip" />
- <!-- Validity Dates: -->
- <TextView
- android:id="@+id/validity_header"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:text="@string/validity_period"
- android:textStyle="bold"
- android:layout_marginLeft="20dip"
- android:layout_marginRight="20dip"
- android:layout_marginBottom="10dip" />
-
- <!-- Issued On: -->
- <TextView
- android:id="@+id/issued_on_header"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/issued_on"
- android:gravity="left"
- android:layout_marginLeft="20dip"
- android:layout_marginRight="20dip" />
-
- <TextView
- android:id="@+id/issued_on"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:gravity="left"
- android:layout_marginLeft="20dip"
- android:layout_marginRight="20dip"
- android:layout_marginBottom="10dip" />
-
- <!-- Expires On: -->
- <TextView
- android:id="@+id/expires_on_header"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/expires_on"
- android:gravity="left"
- android:layout_marginLeft="20dip"
- android:layout_marginRight="20dip" />
-
- <TextView
- android:id="@+id/expires_on"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:gravity="left"
- android:layout_marginLeft="20dip"
- android:layout_marginRight="20dip"
- android:layout_marginBottom="12dip" />
-
- </TableLayout>
+ <TextView
+ android:id="@+id/sha1_fingerprint"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_marginBottom="7dip" />
</LinearLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index c84a591..aa8c510 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4586,6 +4586,25 @@
for more info. -->
<attr name="actionViewClass" format="string" />
+ <!-- The name of an optional ActionProvider class to instantiate an action view
+ and perform operations such as default action for that menu item.
+ See {@link android.view.MenuItem#setActionProvider(android.view.ActionProvider)}
+ for more info. -->
+ <attr name="actionProviderClass" format="string" />
+
+ </declare-styleable>
+
+ <!-- Attrbitutes for a ActvityChooserView. -->
+ <declare-styleable name="ActivityChooserView">
+ <!-- The maximal number of items initially shown in the activity list. -->
+ <attr name="initialActivityCount" format="string" />
+ <!-- The drawable to show in the button for expanding the activities overflow popup.
+ <strong>Note:</strong> Clients would like to set this drawable
+ as a clue about the action the chosen activity will perform. For
+ example, if share activity is to be chosen the drawable should
+ give a clue that sharing is to be performed.
+ -->
+ <attr name="expandActivityOverflowButtonDrawable" format="reference" />
</declare-styleable>
<!-- **************************************************************** -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 2c10b3d..87b9be4 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -441,18 +441,6 @@
<!-- Enables swipe versus poly-finger touch disambiguation in the KeyboardView -->
<bool name="config_swipeDisambiguation">true</bool>
- <!-- Enables special filtering code in the framework for raw touch events
- from the touch driver. This code exists for one particular device,
- and should not be enabled for any others. Hopefully in the future
- it will be removed when the lower-level touch driver generates better
- data. -->
- <bool name="config_filterTouchEvents">false</bool>
-
- <!-- Enables special filtering code in the framework for raw touch events
- from the touch driver. This code exists for one particular device,
- and should not be enabled for any others. -->
- <bool name="config_filterJumpyTouchEvents">false</bool>
-
<!-- Specifies the amount of time to disable virtual keys after the screen is touched
in order to filter out accidental virtual key presses due to swiping gestures
or taps near the edge of the display. May be 0 to disable the feature.
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 945e0c4..54e484e 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1785,4 +1785,7 @@
<public type="string" name="status_bar_notification_info_overflow" />
<public type="attr" name="textDirection"/>
+
+ <public type="attr" name="actionProviderClass" />
+
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index c8b3b4f..50f8df7 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2799,11 +2799,6 @@
<!-- Do Not Translate: Alternate eri.xml -->
<string name="alternate_eri_file">/data/eri.xml</string>
- <string name="pptp_vpn_description">Point-to-Point Tunneling Protocol</string>
- <string name="l2tp_vpn_description">Layer 2 Tunneling Protocol</string>
- <string name="l2tp_ipsec_psk_vpn_description">Pre-shared key based L2TP/IPSec VPN</string>
- <string name="l2tp_ipsec_crt_vpn_description">Certificate based L2TP/IPSec VPN</string>
-
<!-- The title of the notification when VPN is active. -->
<string name="vpn_title">VPN is activated.</string>
<!-- The title of the notification when VPN is active with an application name. -->
@@ -2931,13 +2926,6 @@
<!-- Dialog action for when there are too many deletes that would take place and we want user confirmation, and the user wants to do nothing for now -->
<string name="sync_do_nothing">Do nothing for now.</string>
- <!-- Title of the VPN service notification: VPN connected [CHAR LIMIT=NONE] -->
- <string name="vpn_notification_title_connected"><xliff:g id="profilename" example="Home PPTP">%s</xliff:g> VPN connected</string>
- <!-- Title of the VPN service notification: VPN disconnected [CHAR LIMIT=NONE] -->
- <string name="vpn_notification_title_disconnected"><xliff:g id="profilename" example="Home PPTP">%s</xliff:g> VPN disconnected</string>
- <!-- Message of the VPN service notification: Hint to reconnect VPN [CHAR LIMIT=NONE] -->
- <string name="vpn_notification_hint_disconnected">Touch to reconnect to a VPN.</string>
-
<!-- Choose Account Activity label -->
<string name="choose_account_label">Select an account</string>
@@ -3001,5 +2989,20 @@
<string name="issued_on">Issued on:</string>
<!-- Label for an information field on an SSL Certificate Dialog -->
<string name="expires_on">Expires on:</string>
+ <!-- Label for an information field on an SSL Certificate Dialog -->
+ <string name="serial_number">Serial number:</string>
+ <!-- Label for an information field on an SSL Certificate Dialog -->
+ <string name="fingerprints">Fingerprints:</string>
+ <!-- Label for an information field on an SSL Certificate Dialog -->
+ <string name="sha256_fingerprint">SHA-256 fingerprint:</string>
+ <!-- Label for an information field on an SSL Certificate Dialog -->
+ <string name="sha1_fingerprint">SHA-1 fingerprint:</string>
+
+ <!-- Title for a button to expand the list of activities in ActivityChooserView [CHAR LIMIT=25] -->
+ <string name="activity_chooser_view_see_all">See all...</string>
+ <!-- Title for a message that there are no activities in ActivityChooserView [CHAR LIMIT=25] -->
+ <string name="activity_chooser_view_no_activities">No activities</string>
+ <!-- Title for a message that prompts selection of a default share handler in ActivityChooserView [CHAR LIMIT=25] -->
+ <string name="activity_chooser_view_select_default">Select default</string>
</resources>
diff --git a/docs/html/guide/topics/graphics/index.jd b/docs/html/guide/topics/graphics/index.jd
index be1b0fc..2490e39 100644
--- a/docs/html/guide/topics/graphics/index.jd
+++ b/docs/html/guide/topics/graphics/index.jd
@@ -14,13 +14,20 @@
</ol>
</li>
</ol>
+ <h2>See also</h2>
+ <ol>
+ <li><a href="{@docRoot}guide/topics/graphics/opengl.html">3D with OpenGL</a></li>
+ <li><a href="{@docRoot}guide/topics/renderscript/index.html">RenderScript</a></li>
+ </ol>
</div>
</div>
-<p>Android graphics are powered by a custom 2D graphics library and OpenGL ES 1.0
-for high performance 3D graphics. The most common 2D graphics APIs can be found in the
-{@link android.graphics.drawable drawable package}. OpenGL APIs are available
-from the Khronos {@link javax.microedition.khronos.opengles OpenGL ES package},
-plus some Android {@link android.opengl OpenGL utilities}.</p>
+
+<p>Android graphics are powered by a custom 2D graphics library, and the framework provides
+support for high performance 3D graphics in the form of OpenGL ES and RenderScript. The most
+common 2D graphics APIs can be found in the {@link android.graphics.drawable drawable package}.
+OpenGL APIs are available from the Khronos {@link javax.microedition.khronos.opengles OpenGL ES} and
+the {@link android.opengl} packages. The RenderScript APIs are available in the
+{@link android.renderscript} package.</p>
<p>When starting a project, it's important to consider exactly what your graphical demands will be.
Varying graphical tasks are best accomplished with varying techniques. For example, graphics and animations
@@ -32,10 +39,10 @@
<p>If you're specifically looking for information on drawing 3D graphics, this page won't
help a lot. However, the information below about how to <a href="#draw-with-canvas">Draw with a
-Canvas</a> (and the section on SurfaceView),
-will give you a quick idea of how you should draw to the View hierarchy. For more information
-on Android's 3D graphic utilities (provided by the OpenGL ES API),
-read <a href="opengl.html">3D with OpenGL</a> and refer to other OpenGL documentation.</p>
+Canvas</a> (and the section on SurfaceView), will give you a quick idea of how you should draw to
+the View hierarchy. For more information on Android's 3D graphics APIs, see
+the <a href="opengl.html">3D with OpenGL</a> and
+<a href="{@docRoot}guide/topics/renderscript/index.html">RenderScript</a> documents.</p>
<h2 id="options">Consider your Options</h2>
diff --git a/docs/html/guide/topics/graphics/opengl.jd b/docs/html/guide/topics/graphics/opengl.jd
index 9f88954..cc467f2 100644
--- a/docs/html/guide/topics/graphics/opengl.jd
+++ b/docs/html/guide/topics/graphics/opengl.jd
@@ -3,51 +3,215 @@
parent.link=index.html
@jd:body
+<div id="qv-wrapper">
+ <div id="qv">
+ <h2>In this document</h2>
+
+ <ol>
+ <li><a href="#basics">The Basics</a></li>
+ <li><a href="#compatibility">OpenGL Versions and Device Compatibility</a>
+ <ol>
+ <li><a href="#textures">Texture Compression Support</a></li>
+ <li><a href="#declare-compression">Declaring Use of Compressed Textures</a></li>
+ </ol>
+ </li>
+ </ol>
+ <h2>Key classes</h2>
+ <ol>
+ <li>{@link android.opengl.GLSurfaceView}</li>
+ <li>{@link android.opengl.GLSurfaceView.Renderer}</li>
+ <li>{@link javax.microedition.khronos.opengles}</li>
+ <li>{@link android.opengl}</li>
+ </ol>
+ <h2>Related Samples</h2>
+ <ol>
+ <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/graphics/
+GLSurfaceViewActivity.html">GLSurfaceViewActivity</a></li>
+ <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/graphics/
+GLES20Activity.html">GLES20Activity</a></li>
+ <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/graphics/
+TouchRotateActivity.html">TouchRotateActivity</a></li>
+ <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/graphics/
+CompressedTextureActivity.html">Compressed Textures</a></li>
+ </ol>
+ <h2>See also</h2>
+ <ol>
+ <li><a href="{@docRoot}resources/articles/glsurfaceview.html">Introducing
+GLSurfaceView</a></li>
+ <li><a href="http://www.khronos.org/opengles/">OpenGL ES</a></li>
+ <li><a href="http://www.khronos.org/opengles/1_X/">OpenGL ES 1.x Specification</a></li>
+ <li><a href="http://www.khronos.org/opengles/2_X/">OpenGL ES 2.x specification</a></li>
+ </ol>
+ </div>
+</div>
-<p>Android includes support for high performance 3D graphics
-via the OpenGL API—specifically, the OpenGL ES API.</p>
+<p>Android includes support for high performance 2D and 3D graphics with the Open Graphics Library
+(OpenGL) API—specifically, the OpenGL ES API. OpenGL is a cross-platform graphics API that
+specifies a standard software interface for 3D graphics processing hardware. OpenGL ES is a flavor
+of the OpenGL specification intended for embedded devices. The OpenGL ES 1.0 and 1.1 API
+specifications have been supported since Android 1.0. Beginning with Android 2.2 (API
+Level 8), the framework supports the OpenGL ES 2.0 API specification.</p>
-<p>OpenGL ES is a flavor of the OpenGL specification intended for embedded devices. Versions of <a
-href="http://www.khronos.org/opengles/">OpenGL ES</a> are loosely peered to versions of the primary
-OpenGL standard. Beginning with Android 2.2, the platform supports OpenGL ES 2.0 (with
-backward compatibility support for OpenGL ES 1.1). For information about the relative number of
-Android-powered devices that support a given version of OpenGL ES, see the <a
-href="http://developer.android.com/resources/dashboard/opengl.html">OpenGL ES Versions</a>
-dashboard.</p>
+<p class="note"><b>Note:</b> The specific API provided by the Android framework is similar to the
+ J2ME JSR239 OpenGL ES API, but is not identical. If you are familiar with J2ME JSR239
+ specification, be alert for variations.</p>
-<p>The specific API provided by Android is similar to the J2ME JSR239 OpenGL
-ES API. However, it may not be identical, so watch out for deviations.</p>
-<h2>Using the API</h2>
+<h2 id="basics">The Basics</h2>
-<p>Here's how to use the API at an extremely high level:</p>
+<p>Android supports OpenGL both through its framework API and the Native Development
+Kit (NDK). This topic focuses on the Android framework interfaces. For more information about the
+NDK, see the <a href="{@docRoot}sdk/ndk/index.html">Android NDK</a>.
-<ol>
-<li>Write a custom {@link android.view.View} subclass.</li>
-<li>Obtain a handle to an OpenGLContext, which provides access to the OpenGL functionality.</li>
-<li>In your View's {@link android.view.View#onDraw onDraw()} method, get a handle to a GL object,
-and use its methods to perform GL operations.</li>
-</ol>
-
-<p>Several samples using OpenGL ES are available in the <a
-href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/graphics/index.html">API
-Demos</a> sample application.
+<p>
+ There are two foundational classes in the Android framework that let you create and manipulate
+graphics with the OpenGL ES API: {@link android.opengl.GLSurfaceView} and {@link
+android.opengl.GLSurfaceView.Renderer}. If your goal is to use OpenGL in your Android application,
+understanding how to implement these classes in an activity should be your first objective.
</p>
-<p>A summary of how to actually write 3D applications using OpenGL is
-beyond the scope of this text and is left as an exercise for the reader.</p>
+<dl>
+ <dt>{@link android.opengl.GLSurfaceView}</dt>
+ <dd>This class is a container on which you can draw and manipulate objects using OpenGL API calls.
+ This class is similar in function to a {@link android.view.SurfaceView}, except that it is
+ specifically for use with OpenGL. You can use this class by simply creating an instance of
+ {@link android.opengl.GLSurfaceView} and adding your
+ {@link android.opengl.GLSurfaceView.Renderer Renderer} to it. However, if you want to capture
+ touch screen events, you should extend the {@link android.opengl.GLSurfaceView} class to
+ implement the touch listeners, as shown in the <a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/graphics/TouchRotateActivity
+.html">TouchRotateActivity</a> sample.</dd>
+
+ <dt>{@link android.opengl.GLSurfaceView.Renderer}</dt>
+ <dd>This interface defines the methods required for drawing graphics in an OpenGL {@link
+ android.opengl.GLSurfaceView}. You must provide an implementation of this interface as a
+ separate class and attach it to your {@link android.opengl.GLSurfaceView} instance using
+ {@link android.opengl.GLSurfaceView#setRenderer(android.opengl.GLSurfaceView.Renderer)
+ GLSurfaceView.setRenderer()}.
+
+ <p>The {@link android.opengl.GLSurfaceView.Renderer} interface requires that you implement the
+ following methods:</p>
+ <ul>
+ <li>
+ {@link
+ android.opengl.GLSurfaceView.Renderer#onSurfaceCreated(javax.microedition.khronos.opengles.GL10,
+ javax.microedition.khronos.egl.EGLConfig) onSurfaceCreated()}: The system calls this
+ method once, when creating the {@link android.opengl.GLSurfaceView}. Use this method to perform
+ actions that need to happen only once, such as setting OpenGL environment parameters or
+ initializing OpenGL graphic objects.
+ </li>
+ <li>
+ {@link
+ android.opengl.GLSurfaceView.Renderer#onDrawFrame(javax.microedition.khronos.opengles.GL10)
+ onDrawFrame()}: The system calls this method on each redraw of the {@link
+ android.opengl.GLSurfaceView}. Use this method as the primary execution point for
+ drawing (and re-drawing) graphic objects.</li>
+ <li>
+ {@link
+ android.opengl.GLSurfaceView.Renderer#onSurfaceChanged(javax.microedition.khronos.opengles.GL10,
+ int, int) onSurfaceChanged()}: The system calls this method when the {@link
+ android.opengl.GLSurfaceView} geometry changes, including changes in size of the {@link
+ android.opengl.GLSurfaceView} or orientation of the device screen. For example, the system calls
+ this method when the device changes from portrait to landscape orientation. Use this method to
+ respond to changes in the {@link android.opengl.GLSurfaceView} container.
+ </li>
+ </ul>
+ </dd>
+</dl>
-<h2>Links to Additional Information</h2>
+<p>Once you have established a container view for OpenGL using {@link
+android.opengl.GLSurfaceView} and {@link android.opengl.GLSurfaceView.Renderer}, you can begin
+calling OpenGL APIs using the following classes:</p>
-<p>Information about OpenGL ES can be
-found at <a title="http://www.khronos.org/opengles/"
-href="http://www.khronos.org/opengles/">http://www.khronos.org/opengles/</a>.</p>
+<ul>
+ <li>OpenGL ES 1.0/1.1 API Packages
+ <ul>
+ <li>{@link javax.microedition.khronos.opengles} - This package provides the standard
+implementation of OpenGL ES 1.0 and 1.1.
+ <ul>
+ <li>{@link javax.microedition.khronos.opengles.GL10}</li>
+ <li>{@link javax.microedition.khronos.opengles.GL10Ext}</li>
+ <li>{@link javax.microedition.khronos.opengles.GL11}</li>
+ <li>{@link javax.microedition.khronos.opengles.GL11Ext}</li>
+ <li>{@link javax.microedition.khronos.opengles.GL11ExtensionPack}</li>
+ </ul>
+ </li>
+ <li>{@link android.opengl} - This package provides a static interface to the OpenGL classes
+ above. These interfaces were added with Android 1.6 (API Level 4).
+ <ul>
+ <li>{@link android.opengl.GLES10}</li>
+ <li>{@link android.opengl.GLES10Ext}</li>
+ <li>{@link android.opengl.GLES11}</li>
+ <li>{@link android.opengl.GLES10Ext}</li>
+ </ul>
+ </li>
+ </ul>
+ </li>
+ <li>OpenGL ES 2.0 API Class
+ <ul>
+ <li>{@link android.opengl.GLES20 android.opengl.GLES20}</li>
+ </ul>
+ </li>
+</ul>
-<p>Information specifically
-about OpenGL ES 1.0 (including a detailed specification) can be found
-at <a title="http://www.khronos.org/opengles/1_X/"
-href="http://www.khronos.org/opengles/1_X/">http://www.khronos.org/opengles/1_X/</a>.</p>
+<h2 id="compatibility">OpenGL Versions and Device Compatibility</h2>
-<p>The documentation for the Android OpenGL ES implementations are available in {@link
-android.opengl} and {@link javax.microedition.khronos.opengles}.</p>
+<p>
+ The OpenGL ES 1.0 and 1.1 API specifications have been supported since Android 1.0.
+Beginning with Android 2.2 (API Level 8), the framework supports the OpenGL ES 2.0 API
+specification. OpenGL ES 2.0 is supported by most Android devices and is recommended for new
+applications being developed with OpenGL. For information about the relative number of
+Android-powered devices that support a given version of OpenGL ES, see the <a
+href="{@docRoot}resources/dashboard/opengl.html">OpenGL ES Versions Dashboard</a>.</p>
+<h3 id="textures">Texture compression support</h3>
+<p>Texture compression can significantly increase the performance of your OpenGL application by
+reducing memory requirements and making more efficient use of memory bandwidth. The Android
+framework provides support for the ETC1 compression format as a standard feature, including a {@link
+android.opengl.ETC1Util} utility class and the {@code etc1tool} compression tool (located in your
+Android SDK at {@code <sdk>/tools/}).</p>
+
+<p>For an example of an Android application that uses texture compression, see the <a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/graphics/
+CompressedTextureActivity.html">CompressedTextureActivity</a> code sample.
+</p>
+
+<p>To check if the ETC1 format is supported on a device, call the {@link
+android.opengl.ETC1Util#isETC1Supported() ETC1Util.isETC1Supported()} method.</p>
+
+<p class="note"><b>Note:</b> The ETC1 texture compression format does not support textures with an
+alpha channel. If your application requires textures with an alpha channel, you should
+investigate other texture compression formats available on your target devices.</p>
+
+<p>Beyond the ETC1 format, Android devices have varied support for texture compression based on
+their GPU chipsets. You should investigate texture compression support on the the devices you are
+are targeting to determine what compression types your application should support.</p>
+
+<p>To determine if texture compression formats other than ETC1 are supported on a particular
+device:</p>
+<ol>
+ <li>Run the following code on your target devices to determine what texture compression
+formats are supported:
+<pre>
+ String extensions = javax.microedition.khronos.opengles.GL10.glGetString(GL10.GL_EXTENSIONS);
+</pre>
+ <p class="warning"><b>Warning:</b> The results of this call vary by device! You must run this
+call on several target devices to determine what compression types are commonly supported on
+your target devices.</p>
+ </li>
+ <li>Review the output of this method to determine what extensions are supported on the
+device.</li>
+</ol>
+
+
+<h3 id="declare-compression">Declaring compressed textures</h3>
+<p>Once you have decided which texture compression types your application will support, you
+must declare them in your manifest file using <a
+href="{@docRoot}guide/topics/manifest/supports-gl-texture-element.html">
+<supports-gl-texture></a>. Declaring this information in your manifest file hides your
+application from users with devices that do not support at least one of your declared
+compression types. For more information on how Android Market filtering works for texture
+compressions, see the <a
+href="{@docRoot}guide/topics/manifest/supports-gl-texture-element.html#market-texture-filtering">
+Android Market and texture compression filtering</a> section of the {@code
+<supports-gl-texture>} documentation.
diff --git a/docs/html/guide/topics/usb/adk.jd b/docs/html/guide/topics/usb/adk.jd
index 2e172f5..b5a3f30 100644
--- a/docs/html/guide/topics/usb/adk.jd
+++ b/docs/html/guide/topics/usb/adk.jd
@@ -326,7 +326,7 @@
<li>To open the firmware code (a sketch), click <strong>File > Open</strong> and select
<code>firmware/demokit/demokit.pde</code>.</li>
- <li>Click <strong>Sketch > Compile/Verify</strong> to ensure that the sketch has no
+ <li>Click <strong>Sketch > Verify/Compile</strong> to ensure that the sketch has no
errors.</li>
<li>Select <strong>File > Upload to I/O Board</strong>. When Arduino outputs <strong>Done
@@ -356,7 +356,8 @@
<li>In the <strong>Project name:</strong> field, type DemoKit.</li>
<li>Choose <strong>Create project from existing source</strong>, click <strong>Browse</strong>,
- select the <code>app</code> directory, and click <strong>Finish</strong>.</li>
+ select the <code>app</code> directory, click <strong>Open</strong> to close that dialog and then
+ click <strong>Finish</strong>.</li>
<li>For Build Target, select <strong>Google APIs</strong> (Platform 2.3.3, API Level 10).
diff --git a/docs/html/resources/resources-data.js b/docs/html/resources/resources-data.js
index e8c9ae7..0fc10bf 100644
--- a/docs/html/resources/resources-data.js
+++ b/docs/html/resources/resources-data.js
@@ -726,6 +726,16 @@
en: 'Binding data to views using XML Adapters examples.'
}
},
+ {
+ tags: ['sample', 'new', 'accessibility'],
+ path: 'samples/TtsEngine/index.html',
+ title: {
+ en: 'Text To Speech Engine'
+ },
+ description: {
+ en: 'An example Text To Speech engine written using the android text to speech engine API.'
+ }
+ },
/////////////////
/// TUTORIALS ///
diff --git a/include/media/mediametadataretriever.h b/include/media/mediametadataretriever.h
index 28f305d..9aa6700 100644
--- a/include/media/mediametadataretriever.h
+++ b/include/media/mediametadataretriever.h
@@ -53,6 +53,7 @@
METADATA_KEY_VIDEO_HEIGHT = 19,
METADATA_KEY_BITRATE = 20,
METADATA_KEY_TIMED_TEXT_LANGUAGES = 21,
+ METADATA_KEY_IS_DRM = 22,
// Add more here...
};
diff --git a/include/media/stagefright/MediaExtractor.h b/include/media/stagefright/MediaExtractor.h
index a82106e..eb45237 100644
--- a/include/media/stagefright/MediaExtractor.h
+++ b/include/media/stagefright/MediaExtractor.h
@@ -56,7 +56,12 @@
virtual uint32_t flags() const;
// for DRM
- virtual void setDrmFlag(bool flag) {};
+ virtual void setDrmFlag(bool flag) {
+ mIsDrm = flag;
+ };
+ virtual bool getDrmFlag() {
+ return mIsDrm;
+ }
virtual char* getDrmTrackInfo(size_t trackID, int *len) {
return NULL;
}
@@ -66,6 +71,8 @@
virtual ~MediaExtractor() {}
private:
+ bool mIsDrm;
+
MediaExtractor(const MediaExtractor &);
MediaExtractor &operator=(const MediaExtractor &);
};
diff --git a/include/surfaceflinger/IGraphicBufferAlloc.h b/include/surfaceflinger/IGraphicBufferAlloc.h
index e1b6b57..d3b2062 100644
--- a/include/surfaceflinger/IGraphicBufferAlloc.h
+++ b/include/surfaceflinger/IGraphicBufferAlloc.h
@@ -37,7 +37,7 @@
/* Create a new GraphicBuffer for the client to use.
*/
virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
- PixelFormat format, uint32_t usage) = 0;
+ PixelFormat format, uint32_t usage, status_t* error) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h
index de748b5..600017e 100644
--- a/include/utils/BitSet.h
+++ b/include/utils/BitSet.h
@@ -44,6 +44,9 @@
// Returns true if the bit set does not contain any marked bits.
inline bool isEmpty() const { return ! value; }
+ // Returns true if the bit set does not contain any unmarked bits.
+ inline bool isFull() const { return value == 0xffffffff; }
+
// Returns true if the specified bit is marked.
inline bool hasBit(uint32_t n) const { return value & valueForBit(n); }
diff --git a/include/utils/threads.h b/include/utils/threads.h
index 0bd69cf..c8e9c04 100644
--- a/include/utils/threads.h
+++ b/include/utils/threads.h
@@ -133,13 +133,13 @@
// Change the scheduling group of a particular thread. The group
// should be one of the ANDROID_TGROUP constants. Returns BAD_VALUE if
// grp is out of range, else another non-zero value with errno set if
-// the operation failed.
+// the operation failed. Thread ID zero means current thread.
extern int androidSetThreadSchedulingGroup(pid_t tid, int grp);
// Change the priority AND scheduling group of a particular thread. The priority
// should be one of the ANDROID_PRIORITY constants. Returns INVALID_OPERATION
// if the priority set failed, else another value if just the group set failed;
-// in either case errno is set.
+// in either case errno is set. Thread ID zero means current thread.
extern int androidSetThreadPriority(pid_t tid, int prio);
#ifdef __cplusplus
@@ -510,6 +510,10 @@
// that case.
status_t requestExitAndWait();
+ // Wait until this object's thread exits. Returns immediately if not yet running.
+ // Do not call from this object's thread; will return WOULD_BLOCK in that case.
+ status_t join();
+
protected:
// exitPending() returns true if requestExit() has been called.
bool exitPending() const;
diff --git a/libs/gui/IGraphicBufferAlloc.cpp b/libs/gui/IGraphicBufferAlloc.cpp
index 0cd51da..30f8d00 100644
--- a/libs/gui/IGraphicBufferAlloc.cpp
+++ b/libs/gui/IGraphicBufferAlloc.cpp
@@ -43,7 +43,7 @@
}
virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
- PixelFormat format, uint32_t usage) {
+ PixelFormat format, uint32_t usage, status_t* error) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferAlloc::getInterfaceDescriptor());
data.writeInt32(w);
@@ -52,14 +52,15 @@
data.writeInt32(usage);
remote()->transact(CREATE_GRAPHIC_BUFFER, data, &reply);
sp<GraphicBuffer> graphicBuffer;
- bool nonNull = (bool)reply.readInt32();
- if (nonNull) {
+ status_t result = reply.readInt32();
+ if (result == NO_ERROR) {
graphicBuffer = new GraphicBuffer();
reply.read(*graphicBuffer);
// reply.readStrongBinder();
// here we don't even have to read the BufferReference from
// the parcel, it'll die with the parcel.
}
+ *error = result;
return graphicBuffer;
}
};
@@ -91,8 +92,10 @@
uint32_t h = data.readInt32();
PixelFormat format = data.readInt32();
uint32_t usage = data.readInt32();
- sp<GraphicBuffer> result(createGraphicBuffer(w, h, format, usage));
- reply->writeInt32(result != 0);
+ status_t error;
+ sp<GraphicBuffer> result =
+ createGraphicBuffer(w, h, format, usage, &error);
+ reply->writeInt32(error);
if (result != 0) {
reply->write(*result);
// We add a BufferReference to this parcel to make sure the
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 3bf6477..886a3fb 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -352,11 +352,13 @@
((uint32_t(buffer->usage) & usage) != usage))
{
usage |= GraphicBuffer::USAGE_HW_TEXTURE;
+ status_t error;
sp<GraphicBuffer> graphicBuffer(
- mGraphicBufferAlloc->createGraphicBuffer(w, h, format, usage));
+ mGraphicBufferAlloc->createGraphicBuffer(
+ w, h, format, usage, &error));
if (graphicBuffer == 0) {
LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer failed");
- return NO_MEMORY;
+ return error;
}
if (updateFormat) {
mPixelFormat = format;
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 450cdf1..ce587b3 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -31,13 +31,13 @@
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
mSurfaceControl = mComposerClient->createSurface(
- String8("Test Surface"), 0, 32, 32, PIXEL_FORMAT_RGB_888, 0);
+ String8("Test Surface"), 0, 32, 32, PIXEL_FORMAT_RGBA_8888, 0);
ASSERT_TRUE(mSurfaceControl != NULL);
ASSERT_TRUE(mSurfaceControl->isValid());
SurfaceComposerClient::openGlobalTransaction();
- ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(30000));
+ ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7fffffff));
ASSERT_EQ(NO_ERROR, mSurfaceControl->show());
SurfaceComposerClient::closeGlobalTransaction();
@@ -84,7 +84,7 @@
PixelFormat fmt=0;
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
ASSERT_EQ(NO_ERROR, sf->captureScreen(0, &heap, &w, &h, &fmt, 64, 64, 0,
- 40000));
+ 0x7fffffff));
ASSERT_TRUE(heap != NULL);
// Set the PROTECTED usage bit and verify that the screenshot fails. Note
@@ -94,6 +94,18 @@
GRALLOC_USAGE_PROTECTED));
ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(anw.get(), 3));
ANativeWindowBuffer* buf = 0;
+
+ status_t err = anw->dequeueBuffer(anw.get(), &buf);
+ if (err) {
+ // we could fail if GRALLOC_USAGE_PROTECTED is not supported.
+ // that's okay as long as this is the reason for the failure.
+ // try again without the GRALLOC_USAGE_PROTECTED bit.
+ ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(), 0));
+ ASSERT_EQ(NO_ERROR, anw->dequeueBuffer(anw.get(), &buf));
+ return;
+ }
+ ASSERT_EQ(NO_ERROR, anw->cancelBuffer(anw.get(), buf));
+
for (int i = 0; i < 4; i++) {
// Loop to make sure SurfaceFlinger has retired a protected buffer.
ASSERT_EQ(NO_ERROR, anw->dequeueBuffer(anw.get(), &buf));
@@ -103,7 +115,7 @@
heap = 0;
w = h = fmt = 0;
ASSERT_EQ(INVALID_OPERATION, sf->captureScreen(0, &heap, &w, &h, &fmt,
- 64, 64, 0, 40000));
+ 64, 64, 0, 0x7fffffff));
ASSERT_TRUE(heap == NULL);
// XXX: This should not be needed, but it seems that the new buffers don't
@@ -126,7 +138,7 @@
heap = 0;
w = h = fmt = 0;
ASSERT_EQ(NO_ERROR, sf->captureScreen(0, &heap, &w, &h, &fmt, 64, 64, 0,
- 40000));
+ 0x7fffffff));
ASSERT_TRUE(heap != NULL);
}
diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp
index 15bb1d2..50312e7 100644
--- a/libs/utils/Threads.cpp
+++ b/libs/utils/Threads.cpp
@@ -316,6 +316,10 @@
#if defined(HAVE_PTHREADS)
pthread_once(&gDoSchedulingGroupOnce, checkDoSchedulingGroup);
if (gDoSchedulingGroup) {
+ // set_sched_policy does not support tid == 0
+ if (tid == 0) {
+ tid = androidGetTid();
+ }
if (set_sched_policy(tid, (grp == ANDROID_TGROUP_BG_NONINTERACT) ?
SP_BACKGROUND : SP_FOREGROUND)) {
return PERMISSION_DENIED;
@@ -842,6 +846,25 @@
return mStatus;
}
+status_t Thread::join()
+{
+ Mutex::Autolock _l(mLock);
+ if (mThread == getThreadId()) {
+ LOGW(
+ "Thread (this=%p): don't call join() from this "
+ "Thread object's thread. It's a guaranteed deadlock!",
+ this);
+
+ return WOULD_BLOCK;
+ }
+
+ while (mRunning == true) {
+ mThreadExitedCondition.wait(mLock);
+ }
+
+ return mStatus;
+}
+
bool Thread::exitPending() const
{
Mutex::Autolock _l(mLock);
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index 7634c6c..6df2f73 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -214,7 +214,7 @@
addFileType("PNG", FILE_TYPE_PNG, "image/png", MtpConstants.FORMAT_PNG);
addFileType("BMP", FILE_TYPE_BMP, "image/x-ms-bmp", MtpConstants.FORMAT_BMP);
addFileType("WBMP", FILE_TYPE_WBMP, "image/vnd.wap.wbmp");
- addFileType("WEBP", FILE_TYPE_WBMP, "image/webp");
+ addFileType("WEBP", FILE_TYPE_WEBP, "image/webp");
addFileType("M3U", FILE_TYPE_M3U, "audio/x-mpegurl", MtpConstants.FORMAT_M3U_PLAYLIST);
addFileType("M3U", FILE_TYPE_M3U, "application/x-mpegurl", MtpConstants.FORMAT_M3U_PLAYLIST);
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 7ebedaf..10694c3 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -453,5 +453,10 @@
* @hide
*/
public static final int METADATA_KEY_TIMED_TEXT_LANGUAGES = 21;
+ /**
+ * If this key exists the media is drm-protected.
+ * @hide
+ */
+ public static final int METADATA_KEY_IS_DRM = 22;
// Add more here...
}
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 790eaa3..c55338a 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -422,6 +422,7 @@
private long mFileSize;
private String mWriter;
private int mCompilation;
+ private boolean mIsDrm;
private boolean mNoMedia; // flag to suppress file from appearing in media tables
public FileCacheEntry beginFile(String path, String mimeType, long lastModified,
@@ -497,6 +498,7 @@
mLastModified = lastModified;
mWriter = null;
mCompilation = 0;
+ mIsDrm = false;
return entry;
}
@@ -599,6 +601,8 @@
mWriter = value.trim();
} else if (name.equalsIgnoreCase("compilation")) {
mCompilation = parseSubstring(value, 0, 0);
+ } else if (name.equalsIgnoreCase("isdrm")) {
+ mIsDrm = (parseSubstring(value, 0, 0) == 1);
}
}
@@ -671,6 +675,7 @@
map.put(MediaStore.MediaColumns.DATE_MODIFIED, mLastModified);
map.put(MediaStore.MediaColumns.SIZE, mFileSize);
map.put(MediaStore.MediaColumns.MIME_TYPE, mMimeType);
+ map.put(MediaStore.MediaColumns.IS_DRM, mIsDrm);
if (!mNoMedia) {
if (MediaFile.isVideoFileType(mFileType)) {
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 8481410..9c0819f 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -487,7 +487,7 @@
private Cursor getInternalRingtones() {
return query(
MediaStore.Audio.Media.INTERNAL_CONTENT_URI, INTERNAL_COLUMNS,
- constructBooleanTrueWhereClause(mFilterColumns),
+ constructBooleanTrueWhereClause(mFilterColumns, mIncludeDrm),
null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
}
@@ -506,7 +506,7 @@
status.equals(Environment.MEDIA_MOUNTED_READ_ONLY))
? query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, MEDIA_COLUMNS,
- constructBooleanTrueWhereClause(mFilterColumns), null,
+ constructBooleanTrueWhereClause(mFilterColumns, mIncludeDrm), null,
MediaStore.Audio.Media.DEFAULT_SORT_ORDER)
: null;
}
@@ -536,11 +536,13 @@
* @param columns The columns that must be true.
* @return The where clause.
*/
- private static String constructBooleanTrueWhereClause(List<String> columns) {
+ private static String constructBooleanTrueWhereClause(List<String> columns, boolean includeDrm) {
if (columns == null) return null;
StringBuilder sb = new StringBuilder();
+ sb.append("(");
+
for (int i = columns.size() - 1; i >= 0; i--) {
sb.append(columns.get(i)).append("=1 or ");
}
@@ -549,7 +551,18 @@
// Remove last ' or '
sb.setLength(sb.length() - 4);
}
-
+
+ sb.append(")");
+
+ if (!includeDrm) {
+ // If not DRM files should be shown, the where clause
+ // will be something like "(is_notification=1) and is_drm=0"
+ sb.append(" and ");
+ sb.append(MediaStore.MediaColumns.IS_DRM);
+ sb.append("=0");
+ }
+
+
return sb.toString();
}
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 5582f92..73a05a5 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -387,10 +387,6 @@
return mInitCheck;
}
-void MPEG4Extractor::setDrmFlag(bool flag) {
- mIsDrm = flag;
-}
-
char* MPEG4Extractor::getDrmTrackInfo(size_t trackID, int *len) {
if (mFirstSINF == NULL) {
return NULL;
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index af0131e..a8023df 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -116,8 +116,13 @@
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC_ADTS)) {
ret = new AACExtractor(source);
}
- if (ret != NULL && isDrm) {
- ret->getMetaData()->setInt32(kKeyIsDRM, 1);
+
+ if (ret != NULL) {
+ if (isDrm) {
+ ret->setDrmFlag(true);
+ } else {
+ ret->setDrmFlag(false);
+ }
}
return ret;
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index f82ff32..89faff7 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -158,6 +158,7 @@
{ "duration", METADATA_KEY_DURATION },
{ "writer", METADATA_KEY_WRITER },
{ "compilation", METADATA_KEY_COMPILATION },
+ { "isdrm", METADATA_KEY_IS_DRM },
};
static const size_t kNumEntries = sizeof(kKeyMap) / sizeof(kKeyMap[0]);
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index de3957b..778c0b5 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -530,6 +530,11 @@
}
}
}
+
+ // To check whether the media file is drm-protected
+ if (mExtractor->getDrmFlag()) {
+ mMetaData.add(METADATA_KEY_IS_DRM, String8("1"));
+ }
}
} // namespace android
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index 3bd4c7e..eae62c6 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -40,7 +40,6 @@
virtual sp<MetaData> getMetaData();
// for DRM
- virtual void setDrmFlag(bool flag);
virtual char* getDrmTrackInfo(size_t trackID, int *len);
protected:
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index f42cbbf..26ea225 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -30,10 +30,6 @@
<service android:name=".screenshot.TakeScreenshotService"
android:exported="false" />
- <activity android:name=".usb.UsbPreferenceActivity"
- android:theme="@*android:style/Theme.Holo.Dialog.Alert"
- android:excludeFromRecents="true">
- </activity>
<activity android:name=".usb.UsbStorageActivity"
android:excludeFromRecents="true">
</activity>
diff --git a/packages/SystemUI/res/layout/usb_preference_buttons.xml b/packages/SystemUI/res/layout/usb_preference_buttons.xml
deleted file mode 100644
index babe07e..0000000
--- a/packages/SystemUI/res/layout/usb_preference_buttons.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- Check box that is displayed in the activity resolver UI for the user
- to make their selection the preferred activity. -->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingLeft="14dip"
- android:paddingRight="15dip"
- android:orientation="vertical">
-
- <Button
- android:id="@+id/mtp_ptp_button"
- android:text="@string/use_ptp_button_title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:focusable="true"
- android:clickable="true" />
-
- <Button
- android:id="@+id/installer_cd_button"
- android:text="@string/installer_cd_button_title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:focusable="true"
- android:clickable="true" />
-
-</LinearLayout>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 70f9b75..882455e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -157,15 +157,6 @@
<!-- Compatibility mode help screen: body text. [CHAR LIMIT=150] -->
<string name="compat_mode_help_body">When an app was designed for a smaller screen, a zoom control will appear by the clock.</string>
- <!-- Title for the USB function chooser in UsbPreferenceActivity. [CHAR LIMIT=30] -->
- <string name="usb_preference_title">USB file transfer options</string>
- <!-- Label for the MTP USB function in UsbPreferenceActivity. [CHAR LIMIT=50] -->
- <string name="use_mtp_button_title">Mount as a media player (MTP)</string>
- <!-- Label for the PTP USB function in UsbPreferenceActivity. [CHAR LIMIT=50] -->
- <string name="use_ptp_button_title">Mount as a camera (PTP)</string>
- <!-- Label for the installer CD image option in UsbPreferenceActivity. [CHAR LIMIT=50] -->
- <string name="installer_cd_button_title">Install Android File Transfer application for Mac</string>
-
<!-- toast message displayed when a screenshot is saved to the Gallery. -->
<string name="screenshot_saving_toast">Screenshot saved to Gallery</string>
<!-- toast message displayed when we fail to take a screenshot. -->
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPreferenceActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPreferenceActivity.java
deleted file mode 100644
index 60906a1..0000000
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbPreferenceActivity.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.usb;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.hardware.usb.UsbManager;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.util.Log;
-import android.widget.Button;
-
-import java.io.File;
-
-import com.android.systemui.R;
-
-public class UsbPreferenceActivity extends Activity implements View.OnClickListener {
-
- private static final String TAG = "UsbPreferenceActivity";
-
- private UsbManager mUsbManager;
- private String mCurrentFunction;
- private String[] mFunctions;
- private String mInstallerImagePath;
- private AlertDialog mDialog;
- private Button mMtpPtpButton;
- private Button mInstallerCdButton;
- private boolean mPtpActive;
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
-
- AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
- dialogBuilder.setTitle(getString(R.string.usb_preference_title));
-
- LayoutInflater inflater = (LayoutInflater)getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- View buttonView = inflater.inflate(R.layout.usb_preference_buttons, null);
- dialogBuilder.setView(buttonView);
- mMtpPtpButton = (Button)buttonView.findViewById(R.id.mtp_ptp_button);
- mInstallerCdButton = (Button)buttonView.findViewById(R.id.installer_cd_button);
- mMtpPtpButton.setOnClickListener(this);
- mInstallerCdButton.setOnClickListener(this);
-
- mPtpActive = mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_PTP);
- if (mPtpActive) {
- mMtpPtpButton.setText(R.string.use_mtp_button_title);
- }
-
- mInstallerImagePath = getString(com.android.internal.R.string.config_isoImagePath);
- if (!(new File(mInstallerImagePath)).exists()) {
- mInstallerCdButton.setVisibility(View.GONE);
- }
-
- mDialog = dialogBuilder.show();
- }
-
- public void onClick(View v) {
- if (v.equals(mMtpPtpButton)) {
- if (mPtpActive) {
- mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_MTP, true);
- } else {
- mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_PTP, true);
- }
- } else if (v.equals(mInstallerCdButton)) {
- // installer CD is never default
- mUsbManager.setCurrentFunction(UsbManager.USB_FUNCTION_MASS_STORAGE, false);
- mUsbManager.setMassStorageBackingFile(mInstallerImagePath);
- }
-
- if (mDialog != null) {
- mDialog.dismiss();
- }
- finish();
- }
-}
diff --git a/packages/VpnDialogs/res/layout/confirm.xml b/packages/VpnDialogs/res/layout/confirm.xml
index 249b6e6..5ab6ee2 100644
--- a/packages/VpnDialogs/res/layout/confirm.xml
+++ b/packages/VpnDialogs/res/layout/confirm.xml
@@ -17,7 +17,8 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
- android:layout_height="wrap_content">
+ android:layout_height="wrap_content"
+ android:padding="5mm">
<ImageView android:id="@+id/icon"
android:layout_width="@android:dimen/app_icon_size"
@@ -32,7 +33,7 @@
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_below="@id/icon"
- android:padding="2mm"
+ android:padding="3mm"
android:text="@string/warning"
android:textSize="18sp"/>
@@ -53,9 +54,7 @@
android:layout_alignParentRight="true"
android:layout_below="@id/warning"
android:text="@string/accept"
-
-
- android:textSize="18sp"
+ android:textSize="20sp"
android:checked="false"/>
</RelativeLayout>
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index c54e719..c7b4a5f 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -36,7 +36,7 @@
DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
private static final String TAG = "VpnConfirm";
- private String mPackageName;
+ private String mPackage;
private IConnectivityManager mService;
@@ -47,19 +47,19 @@
protected void onResume() {
super.onResume();
try {
- mPackageName = getCallingPackage();
+ mPackage = getCallingPackage();
mService = IConnectivityManager.Stub.asInterface(
ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
- if (mPackageName.equals(mService.prepareVpn(null))) {
+ if (mService.prepareVpn(mPackage, null)) {
setResult(RESULT_OK);
finish();
return;
}
PackageManager pm = getPackageManager();
- ApplicationInfo app = pm.getApplicationInfo(mPackageName, 0);
+ ApplicationInfo app = pm.getApplicationInfo(mPackage, 0);
View view = View.inflate(this, R.layout.confirm, null);
((ImageView) view.findViewById(R.id.icon)).setImageDrawable(app.loadIcon(pm));
@@ -103,8 +103,7 @@
@Override
public void onClick(DialogInterface dialog, int which) {
try {
- if (which == AlertDialog.BUTTON_POSITIVE &&
- mPackageName.equals(mService.prepareVpn(mPackageName))) {
+ if (which == AlertDialog.BUTTON_POSITIVE && mService.prepareVpn(null, mPackage)) {
setResult(RESULT_OK);
}
} catch (Exception e) {
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
index c076ba0..21e916b 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
@@ -65,15 +65,14 @@
ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
View view = View.inflate(this, R.layout.manage, null);
- if (mConfig.sessionName != null) {
- ((TextView) view.findViewById(R.id.session)).setText(mConfig.sessionName);
+ if (mConfig.session != null) {
+ ((TextView) view.findViewById(R.id.session)).setText(mConfig.session);
}
mDuration = (TextView) view.findViewById(R.id.duration);
mDataTransmitted = (TextView) view.findViewById(R.id.data_transmitted);
mDataReceived = (TextView) view.findViewById(R.id.data_received);
- if (mConfig.packageName == null) {
- // Legacy VPN does not have a package name.
+ if (mConfig.packagz.equals(VpnConfig.LEGACY_VPN)) {
mDialog = new AlertDialog.Builder(this)
.setIcon(android.R.drawable.ic_dialog_info)
.setTitle(R.string.legacy_title)
@@ -83,7 +82,7 @@
.create();
} else {
PackageManager pm = getPackageManager();
- ApplicationInfo app = pm.getApplicationInfo(mConfig.packageName, 0);
+ ApplicationInfo app = pm.getApplicationInfo(mConfig.packagz, 0);
mDialog = new AlertDialog.Builder(this)
.setIcon(app.loadIcon(pm))
@@ -126,7 +125,7 @@
if (which == AlertDialog.BUTTON_POSITIVE) {
mConfig.configureIntent.send();
} else if (which == AlertDialog.BUTTON_NEUTRAL) {
- mService.prepareVpn("");
+ mService.prepareVpn(mConfig.packagz, VpnConfig.LEGACY_VPN);
}
} catch (Exception e) {
Log.e(TAG, "onClick", e);
@@ -170,7 +169,7 @@
try {
// See dev_seq_printf_stats() in net/core/dev.c.
in = new DataInputStream(new FileInputStream("/proc/net/dev"));
- String prefix = mConfig.interfaceName + ':';
+ String prefix = mConfig.interfaze + ':';
while (true) {
String line = in.readLine().trim();
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 79218a5..49cb864 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -768,10 +768,6 @@
dump.append(mConfig.excludedDeviceNames.itemAt(i).string());
}
dump.append("]\n");
- dump.appendFormat(INDENT2 "FilterTouchEvents: %s\n",
- toString(mConfig.filterTouchEvents));
- dump.appendFormat(INDENT2 "FilterJumpyTouchEvents: %s\n",
- toString(mConfig.filterJumpyTouchEvents));
dump.appendFormat(INDENT2 "VirtualKeyQuietTime: %0.1fms\n",
mConfig.virtualKeyQuietTime * 0.000001f);
@@ -1955,13 +1951,6 @@
mLastTouch.clear();
mDownTime = 0;
- for (uint32_t i = 0; i < MAX_POINTERS; i++) {
- mAveragingTouchFilter.historyStart[i] = 0;
- mAveragingTouchFilter.historyEnd[i] = 0;
- }
-
- mJumpyTouchFilter.jumpyPointsDropped = 0;
-
mLocked.currentVirtualKey.down = false;
mLocked.orientedRanges.havePressure = false;
@@ -2028,10 +2017,6 @@
}
void TouchInputMapper::configureParameters() {
- mParameters.useBadTouchFilter = mConfig.filterTouchEvents;
- mParameters.useAveragingTouchFilter = mConfig.filterTouchEvents;
- mParameters.useJumpyTouchFilter = mConfig.filterJumpyTouchEvents;
-
// Use the pointer presentation mode for devices that do not support distinct
// multitouch. The spot-based presentation relies on being able to accurately
// locate two or more fingers on the touch pad.
@@ -2122,13 +2107,6 @@
mParameters.associatedDisplayId);
dump.appendFormat(INDENT4 "OrientationAware: %s\n",
toString(mParameters.orientationAware));
-
- dump.appendFormat(INDENT4 "UseBadTouchFilter: %s\n",
- toString(mParameters.useBadTouchFilter));
- dump.appendFormat(INDENT4 "UseAveragingTouchFilter: %s\n",
- toString(mParameters.useAveragingTouchFilter));
- dump.appendFormat(INDENT4 "UseJumpyTouchFilter: %s\n",
- toString(mParameters.useJumpyTouchFilter));
}
void TouchInputMapper::configureRawAxes() {
@@ -2985,33 +2963,10 @@
#endif
// Preprocess pointer data.
- if (mParameters.useBadTouchFilter) {
- if (applyBadTouchFilter()) {
- havePointerIds = false;
- }
- }
-
- if (mParameters.useJumpyTouchFilter) {
- if (applyJumpyTouchFilter()) {
- havePointerIds = false;
- }
- }
-
if (!havePointerIds) {
calculatePointerIds();
}
- TouchData temp;
- TouchData* savedTouch;
- if (mParameters.useAveragingTouchFilter) {
- temp.copyFrom(mCurrentTouch);
- savedTouch = & temp;
-
- applyAveragingTouchFilter();
- } else {
- savedTouch = & mCurrentTouch;
- }
-
uint32_t policyFlags = 0;
if (mLastTouch.pointerCount == 0 && mCurrentTouch.pointerCount != 0) {
if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) {
@@ -3058,9 +3013,9 @@
// Keep the button state so we can track edge-triggered button state changes.
if (touchResult == DROP_STROKE) {
mLastTouch.clear();
- mLastTouch.buttonState = savedTouch->buttonState;
+ mLastTouch.buttonState = mCurrentTouch.buttonState;
} else {
- mLastTouch.copyFrom(*savedTouch);
+ mLastTouch.copyFrom(mCurrentTouch);
}
}
@@ -4826,359 +4781,6 @@
}
}
-/* Special hack for devices that have bad screen data: if one of the
- * points has moved more than a screen height from the last position,
- * then drop it. */
-bool TouchInputMapper::applyBadTouchFilter() {
- uint32_t pointerCount = mCurrentTouch.pointerCount;
-
- // Nothing to do if there are no points.
- if (pointerCount == 0) {
- return false;
- }
-
- // Don't do anything if a finger is going down or up. We run
- // here before assigning pointer IDs, so there isn't a good
- // way to do per-finger matching.
- if (pointerCount != mLastTouch.pointerCount) {
- return false;
- }
-
- // We consider a single movement across more than a 7/16 of
- // the long size of the screen to be bad. This was a magic value
- // determined by looking at the maximum distance it is feasible
- // to actually move in one sample.
- int32_t maxDeltaY = (mRawAxes.y.maxValue - mRawAxes.y.minValue + 1) * 7 / 16;
-
- // XXX The original code in InputDevice.java included commented out
- // code for testing the X axis. Note that when we drop a point
- // we don't actually restore the old X either. Strange.
- // The old code also tries to track when bad points were previously
- // detected but it turns out that due to the placement of a "break"
- // at the end of the loop, we never set mDroppedBadPoint to true
- // so it is effectively dead code.
- // Need to figure out if the old code is busted or just overcomplicated
- // but working as intended.
-
- // Look through all new points and see if any are farther than
- // acceptable from all previous points.
- for (uint32_t i = pointerCount; i-- > 0; ) {
- int32_t y = mCurrentTouch.pointers[i].y;
- int32_t closestY = INT_MAX;
- int32_t closestDeltaY = 0;
-
-#if DEBUG_HACKS
- LOGD("BadTouchFilter: Looking at next point #%d: y=%d", i, y);
-#endif
-
- for (uint32_t j = pointerCount; j-- > 0; ) {
- int32_t lastY = mLastTouch.pointers[j].y;
- int32_t deltaY = abs(y - lastY);
-
-#if DEBUG_HACKS
- LOGD("BadTouchFilter: Comparing with last point #%d: y=%d deltaY=%d",
- j, lastY, deltaY);
-#endif
-
- if (deltaY < maxDeltaY) {
- goto SkipSufficientlyClosePoint;
- }
- if (deltaY < closestDeltaY) {
- closestDeltaY = deltaY;
- closestY = lastY;
- }
- }
-
- // Must not have found a close enough match.
-#if DEBUG_HACKS
- LOGD("BadTouchFilter: Dropping bad point #%d: newY=%d oldY=%d deltaY=%d maxDeltaY=%d",
- i, y, closestY, closestDeltaY, maxDeltaY);
-#endif
-
- mCurrentTouch.pointers[i].y = closestY;
- return true; // XXX original code only corrects one point
-
- SkipSufficientlyClosePoint: ;
- }
-
- // No change.
- return false;
-}
-
-/* Special hack for devices that have bad screen data: drop points where
- * the coordinate value for one axis has jumped to the other pointer's location.
- */
-bool TouchInputMapper::applyJumpyTouchFilter() {
- uint32_t pointerCount = mCurrentTouch.pointerCount;
- if (mLastTouch.pointerCount != pointerCount) {
-#if DEBUG_HACKS
- LOGD("JumpyTouchFilter: Different pointer count %d -> %d",
- mLastTouch.pointerCount, pointerCount);
- for (uint32_t i = 0; i < pointerCount; i++) {
- LOGD(" Pointer %d (%d, %d)", i,
- mCurrentTouch.pointers[i].x, mCurrentTouch.pointers[i].y);
- }
-#endif
-
- if (mJumpyTouchFilter.jumpyPointsDropped < JUMPY_TRANSITION_DROPS) {
- if (mLastTouch.pointerCount == 1 && pointerCount == 2) {
- // Just drop the first few events going from 1 to 2 pointers.
- // They're bad often enough that they're not worth considering.
- mCurrentTouch.pointerCount = 1;
- mJumpyTouchFilter.jumpyPointsDropped += 1;
-
-#if DEBUG_HACKS
- LOGD("JumpyTouchFilter: Pointer 2 dropped");
-#endif
- return true;
- } else if (mLastTouch.pointerCount == 2 && pointerCount == 1) {
- // The event when we go from 2 -> 1 tends to be messed up too
- mCurrentTouch.pointerCount = 2;
- mCurrentTouch.pointers[0] = mLastTouch.pointers[0];
- mCurrentTouch.pointers[1] = mLastTouch.pointers[1];
- mJumpyTouchFilter.jumpyPointsDropped += 1;
-
-#if DEBUG_HACKS
- for (int32_t i = 0; i < 2; i++) {
- LOGD("JumpyTouchFilter: Pointer %d replaced (%d, %d)", i,
- mCurrentTouch.pointers[i].x, mCurrentTouch.pointers[i].y);
- }
-#endif
- return true;
- }
- }
- // Reset jumpy points dropped on other transitions or if limit exceeded.
- mJumpyTouchFilter.jumpyPointsDropped = 0;
-
-#if DEBUG_HACKS
- LOGD("JumpyTouchFilter: Transition - drop limit reset");
-#endif
- return false;
- }
-
- // We have the same number of pointers as last time.
- // A 'jumpy' point is one where the coordinate value for one axis
- // has jumped to the other pointer's location. No need to do anything
- // else if we only have one pointer.
- if (pointerCount < 2) {
- return false;
- }
-
- if (mJumpyTouchFilter.jumpyPointsDropped < JUMPY_DROP_LIMIT) {
- int jumpyEpsilon = (mRawAxes.y.maxValue - mRawAxes.y.minValue + 1) / JUMPY_EPSILON_DIVISOR;
-
- // We only replace the single worst jumpy point as characterized by pointer distance
- // in a single axis.
- int32_t badPointerIndex = -1;
- int32_t badPointerReplacementIndex = -1;
- int32_t badPointerDistance = INT_MIN; // distance to be corrected
-
- for (uint32_t i = pointerCount; i-- > 0; ) {
- int32_t x = mCurrentTouch.pointers[i].x;
- int32_t y = mCurrentTouch.pointers[i].y;
-
-#if DEBUG_HACKS
- LOGD("JumpyTouchFilter: Point %d (%d, %d)", i, x, y);
-#endif
-
- // Check if a touch point is too close to another's coordinates
- bool dropX = false, dropY = false;
- for (uint32_t j = 0; j < pointerCount; j++) {
- if (i == j) {
- continue;
- }
-
- if (abs(x - mCurrentTouch.pointers[j].x) <= jumpyEpsilon) {
- dropX = true;
- break;
- }
-
- if (abs(y - mCurrentTouch.pointers[j].y) <= jumpyEpsilon) {
- dropY = true;
- break;
- }
- }
- if (! dropX && ! dropY) {
- continue; // not jumpy
- }
-
- // Find a replacement candidate by comparing with older points on the
- // complementary (non-jumpy) axis.
- int32_t distance = INT_MIN; // distance to be corrected
- int32_t replacementIndex = -1;
-
- if (dropX) {
- // X looks too close. Find an older replacement point with a close Y.
- int32_t smallestDeltaY = INT_MAX;
- for (uint32_t j = 0; j < pointerCount; j++) {
- int32_t deltaY = abs(y - mLastTouch.pointers[j].y);
- if (deltaY < smallestDeltaY) {
- smallestDeltaY = deltaY;
- replacementIndex = j;
- }
- }
- distance = abs(x - mLastTouch.pointers[replacementIndex].x);
- } else {
- // Y looks too close. Find an older replacement point with a close X.
- int32_t smallestDeltaX = INT_MAX;
- for (uint32_t j = 0; j < pointerCount; j++) {
- int32_t deltaX = abs(x - mLastTouch.pointers[j].x);
- if (deltaX < smallestDeltaX) {
- smallestDeltaX = deltaX;
- replacementIndex = j;
- }
- }
- distance = abs(y - mLastTouch.pointers[replacementIndex].y);
- }
-
- // If replacing this pointer would correct a worse error than the previous ones
- // considered, then use this replacement instead.
- if (distance > badPointerDistance) {
- badPointerIndex = i;
- badPointerReplacementIndex = replacementIndex;
- badPointerDistance = distance;
- }
- }
-
- // Correct the jumpy pointer if one was found.
- if (badPointerIndex >= 0) {
-#if DEBUG_HACKS
- LOGD("JumpyTouchFilter: Replacing bad pointer %d with (%d, %d)",
- badPointerIndex,
- mLastTouch.pointers[badPointerReplacementIndex].x,
- mLastTouch.pointers[badPointerReplacementIndex].y);
-#endif
-
- mCurrentTouch.pointers[badPointerIndex].x =
- mLastTouch.pointers[badPointerReplacementIndex].x;
- mCurrentTouch.pointers[badPointerIndex].y =
- mLastTouch.pointers[badPointerReplacementIndex].y;
- mJumpyTouchFilter.jumpyPointsDropped += 1;
- return true;
- }
- }
-
- mJumpyTouchFilter.jumpyPointsDropped = 0;
- return false;
-}
-
-/* Special hack for devices that have bad screen data: aggregate and
- * compute averages of the coordinate data, to reduce the amount of
- * jitter seen by applications. */
-void TouchInputMapper::applyAveragingTouchFilter() {
- for (uint32_t currentIndex = 0; currentIndex < mCurrentTouch.pointerCount; currentIndex++) {
- uint32_t id = mCurrentTouch.pointers[currentIndex].id;
- int32_t x = mCurrentTouch.pointers[currentIndex].x;
- int32_t y = mCurrentTouch.pointers[currentIndex].y;
- int32_t pressure;
- switch (mCalibration.pressureSource) {
- case Calibration::PRESSURE_SOURCE_PRESSURE:
- pressure = mCurrentTouch.pointers[currentIndex].pressure;
- break;
- case Calibration::PRESSURE_SOURCE_TOUCH:
- pressure = mCurrentTouch.pointers[currentIndex].touchMajor;
- break;
- default:
- pressure = 1;
- break;
- }
-
- if (mLastTouch.idBits.hasBit(id)) {
- // Pointer was down before and is still down now.
- // Compute average over history trace.
- uint32_t start = mAveragingTouchFilter.historyStart[id];
- uint32_t end = mAveragingTouchFilter.historyEnd[id];
-
- int64_t deltaX = x - mAveragingTouchFilter.historyData[end].pointers[id].x;
- int64_t deltaY = y - mAveragingTouchFilter.historyData[end].pointers[id].y;
- uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY);
-
-#if DEBUG_HACKS
- LOGD("AveragingTouchFilter: Pointer id %d - Distance from last sample: %lld",
- id, distance);
-#endif
-
- if (distance < AVERAGING_DISTANCE_LIMIT) {
- // Increment end index in preparation for recording new historical data.
- end += 1;
- if (end > AVERAGING_HISTORY_SIZE) {
- end = 0;
- }
-
- // If the end index has looped back to the start index then we have filled
- // the historical trace up to the desired size so we drop the historical
- // data at the start of the trace.
- if (end == start) {
- start += 1;
- if (start > AVERAGING_HISTORY_SIZE) {
- start = 0;
- }
- }
-
- // Add the raw data to the historical trace.
- mAveragingTouchFilter.historyStart[id] = start;
- mAveragingTouchFilter.historyEnd[id] = end;
- mAveragingTouchFilter.historyData[end].pointers[id].x = x;
- mAveragingTouchFilter.historyData[end].pointers[id].y = y;
- mAveragingTouchFilter.historyData[end].pointers[id].pressure = pressure;
-
- // Average over all historical positions in the trace by total pressure.
- int32_t averagedX = 0;
- int32_t averagedY = 0;
- int32_t totalPressure = 0;
- for (;;) {
- int32_t historicalX = mAveragingTouchFilter.historyData[start].pointers[id].x;
- int32_t historicalY = mAveragingTouchFilter.historyData[start].pointers[id].y;
- int32_t historicalPressure = mAveragingTouchFilter.historyData[start]
- .pointers[id].pressure;
-
- averagedX += historicalX * historicalPressure;
- averagedY += historicalY * historicalPressure;
- totalPressure += historicalPressure;
-
- if (start == end) {
- break;
- }
-
- start += 1;
- if (start > AVERAGING_HISTORY_SIZE) {
- start = 0;
- }
- }
-
- if (totalPressure != 0) {
- averagedX /= totalPressure;
- averagedY /= totalPressure;
-
-#if DEBUG_HACKS
- LOGD("AveragingTouchFilter: Pointer id %d - "
- "totalPressure=%d, averagedX=%d, averagedY=%d", id, totalPressure,
- averagedX, averagedY);
-#endif
-
- mCurrentTouch.pointers[currentIndex].x = averagedX;
- mCurrentTouch.pointers[currentIndex].y = averagedY;
- }
- } else {
-#if DEBUG_HACKS
- LOGD("AveragingTouchFilter: Pointer id %d - Exceeded max distance", id);
-#endif
- }
- } else {
-#if DEBUG_HACKS
- LOGD("AveragingTouchFilter: Pointer id %d - Pointer went up", id);
-#endif
- }
-
- // Reset pointer history.
- mAveragingTouchFilter.historyStart[id] = 0;
- mAveragingTouchFilter.historyEnd[id] = 0;
- mAveragingTouchFilter.historyData[0].pointers[id].x = x;
- mAveragingTouchFilter.historyData[0].pointers[id].y = y;
- mAveragingTouchFilter.historyData[0].pointers[id].pressure = pressure;
- }
-}
-
int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
{ // acquire lock
AutoMutex _l(mLock);
@@ -5403,6 +5005,7 @@
mAccumulator.clearSlots(mSlotCount);
mAccumulator.clearButtons();
mButtonState = 0;
+ mPointerIdBits.clear();
if (mUsingSlotsProtocol) {
// Query the driver for the current slot index and use it as the initial slot
@@ -5627,28 +5230,32 @@
// Assign pointer id using tracking id if available.
if (havePointerIds) {
- int32_t id;
- if (mUsingSlotsProtocol) {
- id = inIndex;
- } else if (fields & Accumulator::FIELD_ABS_MT_TRACKING_ID) {
- id = inSlot.absMTTrackingId;
- } else {
- id = -1;
- }
+ int32_t id = -1;
+ if (fields & Accumulator::FIELD_ABS_MT_TRACKING_ID) {
+ int32_t trackingId = inSlot.absMTTrackingId;
- if (id >= 0 && id <= MAX_POINTER_ID) {
+ for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty(); ) {
+ uint32_t n = idBits.firstMarkedBit();
+ idBits.clearBit(n);
+
+ if (mPointerTrackingIdMap[n] == trackingId) {
+ id = n;
+ }
+ }
+
+ if (id < 0 && !mPointerIdBits.isFull()) {
+ id = mPointerIdBits.firstUnmarkedBit();
+ mPointerIdBits.markBit(id);
+ mPointerTrackingIdMap[id] = trackingId;
+ }
+ }
+ if (id < 0) {
+ havePointerIds = false;
+ mCurrentTouch.idBits.clear();
+ } else {
outPointer.id = id;
mCurrentTouch.idToIndex[id] = outCount;
mCurrentTouch.idBits.markBit(id);
- } else {
- if (id >= 0) {
-#if DEBUG_POINTERS
- LOGD("Pointers: Ignoring driver provided slot index or tracking id %d because "
- "it is larger than the maximum supported pointer id %d",
- id, MAX_POINTER_ID);
-#endif
- }
- havePointerIds = false;
}
}
@@ -5660,6 +5267,8 @@
mButtonState = (mButtonState | mAccumulator.buttonDown) & ~mAccumulator.buttonUp;
mCurrentTouch.buttonState = mButtonState;
+ mPointerIdBits = mCurrentTouch.idBits;
+
syncTouch(when, havePointerIds);
if (!mUsingSlotsProtocol) {
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 288ff4e..69fa6b4 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -57,14 +57,6 @@
CHANGE_MUST_REOPEN = 1 << 31,
};
- // Determines whether to turn on some hacks we have to improve the touch interaction with a
- // certain device whose screen currently is not all that good.
- bool filterTouchEvents;
-
- // Determines whether to turn on some hacks to improve touch interaction with another device
- // where touch coordinate data can get corrupted.
- bool filterJumpyTouchEvents;
-
// Gets the amount of time to disable virtual keys after the screen is touched
// in order to filter out accidental virtual key presses due to swiping gestures
// or taps near the edge of the display. May be 0 to disable the feature.
@@ -146,8 +138,6 @@
float pointerGestureZoomSpeedRatio;
InputReaderConfiguration() :
- filterTouchEvents(false),
- filterJumpyTouchEvents(false),
virtualKeyQuietTime(0),
pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f),
wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f),
@@ -812,10 +802,6 @@
int32_t associatedDisplayId;
bool orientationAware;
- bool useBadTouchFilter;
- bool useJumpyTouchFilter;
- bool useAveragingTouchFilter;
-
enum GestureMode {
GESTURE_MODE_POINTER,
GESTURE_MODE_SPOTS,
@@ -1042,38 +1028,6 @@
void syncTouch(nsecs_t when, bool havePointerIds);
private:
- /* Maximum number of historical samples to average. */
- static const uint32_t AVERAGING_HISTORY_SIZE = 5;
-
- /* Slop distance for jumpy pointer detection.
- * The vertical range of the screen divided by this is our epsilon value. */
- static const uint32_t JUMPY_EPSILON_DIVISOR = 212;
-
- /* Number of jumpy points to drop for touchscreens that need it. */
- static const uint32_t JUMPY_TRANSITION_DROPS = 3;
- static const uint32_t JUMPY_DROP_LIMIT = 3;
-
- /* Maximum squared distance for averaging.
- * If moving farther than this, turn of averaging to avoid lag in response. */
- static const uint64_t AVERAGING_DISTANCE_LIMIT = 75 * 75;
-
- struct AveragingTouchFilterState {
- // Individual history tracks are stored by pointer id
- uint32_t historyStart[MAX_POINTERS];
- uint32_t historyEnd[MAX_POINTERS];
- struct {
- struct {
- int32_t x;
- int32_t y;
- int32_t pressure;
- } pointers[MAX_POINTERS];
- } historyData[AVERAGING_HISTORY_SIZE];
- } mAveragingTouchFilter;
-
- struct JumpyTouchFilterState {
- uint32_t jumpyPointsDropped;
- } mJumpyTouchFilter;
-
struct PointerDistanceHeapElement {
uint32_t currentPointerIndex : 8;
uint32_t lastPointerIndex : 8;
@@ -1251,9 +1205,6 @@
bool isPointInsideSurfaceLocked(int32_t x, int32_t y);
const VirtualKey* findVirtualKeyHitLocked(int32_t x, int32_t y);
- bool applyBadTouchFilter();
- bool applyJumpyTouchFilter();
- void applyAveragingTouchFilter();
void calculatePointerIds();
};
@@ -1401,6 +1352,10 @@
int32_t mButtonState;
+ // Specifies the pointer id bits that are in use, and their associated tracking id.
+ BitSet32 mPointerIdBits;
+ int32_t mPointerTrackingIdMap[MAX_POINTER_ID + 1];
+
void clearState();
void sync(nsecs_t when);
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index d3c5ece..67067de 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -144,14 +144,6 @@
mDisplayInfos.add(displayId, info);
}
- void setFilterTouchEvents(bool enabled) {
- mConfig.filterTouchEvents = enabled;
- }
-
- void setFilterJumpyTouchEvents(bool enabled) {
- mConfig.filterJumpyTouchEvents = enabled;
- }
-
virtual nsecs_t getVirtualKeyQuietTime() {
return 0;
}
@@ -3536,7 +3528,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
+ ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0));
@@ -3545,9 +3537,9 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
motionArgs.action);
ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
+ ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(2, motionArgs.pointerProperties[1].id);
+ ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0));
@@ -3567,9 +3559,9 @@
ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
+ ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(2, motionArgs.pointerProperties[1].id);
+ ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0));
@@ -3587,9 +3579,9 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
motionArgs.action);
ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
+ ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(2, motionArgs.pointerProperties[1].id);
+ ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0));
@@ -3599,7 +3591,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(2, motionArgs.pointerProperties[0].id);
+ ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
@@ -3614,7 +3606,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(2, motionArgs.pointerProperties[0].id);
+ ASSERT_EQ(1, motionArgs.pointerProperties[0].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
@@ -3630,17 +3622,17 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
motionArgs.action);
ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(2, motionArgs.pointerProperties[0].id);
+ ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(3, motionArgs.pointerProperties[1].id);
+ ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
+ toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
// Second finger up.
x3 += 30; y3 -= 20;
@@ -3650,22 +3642,22 @@
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
motionArgs.action);
ASSERT_EQ(size_t(2), motionArgs.pointerCount);
- ASSERT_EQ(2, motionArgs.pointerProperties[0].id);
+ ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
- ASSERT_EQ(3, motionArgs.pointerProperties[1].id);
+ ASSERT_EQ(1, motionArgs.pointerProperties[1].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[1].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
- toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0));
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1],
+ toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0));
ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(3, motionArgs.pointerProperties[0].id);
+ ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0));
@@ -3677,7 +3669,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
ASSERT_EQ(size_t(1), motionArgs.pointerCount);
- ASSERT_EQ(3, motionArgs.pointerProperties[0].id);
+ ASSERT_EQ(0, motionArgs.pointerProperties[0].id);
ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0));
@@ -3728,7 +3720,7 @@
FakeInputDispatcher::NotifyMotionArgs args;
ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(id, args.pointerProperties[0].id);
+ ASSERT_EQ(0, args.pointerProperties[0].id);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
x, y, pressure, size, touchMajor, touchMinor, toolMajor, toolMinor, orientation));
}
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 663f4f4..b98d2a2 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -44,7 +44,6 @@
import android.net.Proxy;
import android.net.ProxyProperties;
import android.net.RouteInfo;
-import android.net.vpn.VpnManager;
import android.net.wifi.WifiStateTracker;
import android.os.Binder;
import android.os.FileUtils;
@@ -65,6 +64,7 @@
import android.util.Slog;
import android.util.SparseIntArray;
+import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.telephony.Phone;
import com.android.server.connectivity.Tethering;
@@ -496,11 +496,8 @@
mSettingsObserver.observe(mContext);
loadGlobalProxy();
-
- VpnManager.startVpnService(context);
}
-
/**
* Sets the preferred network.
* @param preference the new preference
@@ -2473,8 +2470,8 @@
/**
* Protect a socket from VPN routing rules. This method is used by
- * VpnBuilder and not available in ConnectivityManager. Permission
- * checks are done in Vpn class.
+ * VpnBuilder and not available in ConnectivityManager. Permissions
+ * are checked in Vpn class.
* @hide
*/
@Override
@@ -2484,20 +2481,20 @@
/**
* Prepare for a VPN application. This method is used by VpnDialogs
- * and not available in ConnectivityManager. Permission checks are
- * done in Vpn class.
+ * and not available in ConnectivityManager. Permissions are checked
+ * in Vpn class.
* @hide
*/
@Override
- public String prepareVpn(String packageName) {
- return mVpn.prepare(packageName);
+ public boolean prepareVpn(String oldPackage, String newPackage) {
+ return mVpn.prepare(oldPackage, newPackage);
}
/**
* Configure a TUN interface and return its file descriptor. Parameters
* are encoded and opaque to this class. This method is used by VpnBuilder
- * and not available in ConnectivityManager. Permission checks are done
- * in Vpn class.
+ * and not available in ConnectivityManager. Permissions are checked in
+ * Vpn class.
* @hide
*/
@Override
@@ -2505,6 +2502,28 @@
return mVpn.establish(config);
}
+ /**
+ * Start legacy VPN and return an intent to VpnDialogs. This method is
+ * used by VpnSettings and not available in ConnectivityManager.
+ * Permissions are checked in Vpn class.
+ * @hide
+ */
+ @Override
+ public void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
+ mVpn.startLegacyVpn(config, racoon, mtpd);
+ }
+
+ /**
+ * Return the information of the ongoing legacy VPN. This method is used
+ * by VpnSettings and not available in ConnectivityManager. Permissions
+ * are checked in Vpn class.
+ * @hide
+ */
+ @Override
+ public LegacyVpnInfo getLegacyVpnInfo() {
+ return mVpn.getLegacyVpnInfo();
+ }
+
private String getDefaultInterface() {
if (ConnectivityManager.isNetworkTypeValid(mActiveDefaultNetwork)) {
NetworkStateTracker tracker = mNetTrackers[mActiveDefaultNetwork];
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 14abf80..2d55433 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -1623,8 +1623,11 @@
if (lastImi == null) return null;
try {
final int lastSubtypeHash = Integer.valueOf(lastIme.second);
- return lastImi.getSubtypeAt(getSubtypeIdFromHashCode(
- lastImi, lastSubtypeHash));
+ final int lastSubtypeId = getSubtypeIdFromHashCode(lastImi, lastSubtypeHash);
+ if (lastSubtypeId < 0 || lastSubtypeId >= lastImi.getSubtypeCount()) {
+ return null;
+ }
+ return lastImi.getSubtypeAt(lastSubtypeId);
} catch (NumberFormatException e) {
return null;
}
diff --git a/services/java/com/android/server/LoadAverageService.java b/services/java/com/android/server/LoadAverageService.java
index b6baadb..da9fc99 100644
--- a/services/java/com/android/server/LoadAverageService.java
+++ b/services/java/com/android/server/LoadAverageService.java
@@ -278,14 +278,14 @@
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.RIGHT | Gravity.TOP;
params.setTitle("Load Average");
- WindowManagerImpl wm = (WindowManagerImpl)getSystemService(WINDOW_SERVICE);
+ WindowManager wm = (WindowManager)getSystemService(WINDOW_SERVICE);
wm.addView(mView, params);
}
@Override
public void onDestroy() {
super.onDestroy();
- ((WindowManagerImpl)getSystemService(WINDOW_SERVICE)).removeView(mView);
+ ((WindowManager)getSystemService(WINDOW_SERVICE)).removeView(mView);
mView = null;
}
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index a8be916..c185012 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -37,6 +37,7 @@
import android.util.Log;
import com.android.internal.R;
+import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.server.ConnectivityService.VpnCallback;
@@ -55,9 +56,8 @@
private final Context mContext;
private final VpnCallback mCallback;
- private String mPackageName;
- private String mInterfaceName;
-
+ private String mPackage = VpnConfig.LEGACY_VPN;
+ private String mInterface;
private LegacyVpnRunner mLegacyVpnRunner;
public Vpn(Context context, VpnCallback callback) {
@@ -66,68 +66,16 @@
}
/**
- * Prepare for a VPN application.
- *
- * @param packageName The package name of the new VPN application.
- * @return The name of the current prepared package.
- */
- public synchronized String prepare(String packageName) {
- // Return the current prepared package if the new one is null.
- if (packageName == null) {
- return mPackageName;
- }
-
- // Only system user can call this method.
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- throw new SecurityException("Unauthorized Caller");
- }
-
- // Check the permission of the given package.
- PackageManager pm = mContext.getPackageManager();
- if (packageName.isEmpty()) {
- packageName = null;
- } else if (pm.checkPermission(VPN, packageName) != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException(packageName + " does not have " + VPN);
- }
-
- // Reset the interface and hide the notification.
- if (mInterfaceName != null) {
- jniResetInterface(mInterfaceName);
- mCallback.restore();
- hideNotification();
- mInterfaceName = null;
- }
-
- // Notify the package being revoked.
- if (mPackageName != null) {
- Intent intent = new Intent(VpnConfig.ACTION_VPN_REVOKED);
- intent.setPackage(mPackageName);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mContext.sendBroadcast(intent);
- }
-
- // Stop legacy VPN if it has been started.
- if (mLegacyVpnRunner != null) {
- mLegacyVpnRunner.exit();
- mLegacyVpnRunner = null;
- }
-
- Log.i(TAG, "Switched from " + mPackageName + " to " + packageName);
- mPackageName = packageName;
- return mPackageName;
- }
-
- /**
* Protect a socket from routing changes by binding it to the given
* interface. The socket IS closed by this method.
*
* @param socket The socket to be bound.
* @param name The name of the interface.
*/
- public void protect(ParcelFileDescriptor socket, String name) {
+ public void protect(ParcelFileDescriptor socket, String interfaze) {
try {
mContext.enforceCallingPermission(VPN, "protect");
- jniProtectSocket(socket.getFd(), name);
+ jniProtect(socket.getFd(), interfaze);
} finally {
try {
socket.close();
@@ -138,10 +86,78 @@
}
/**
- * Configure a TUN interface and return its file descriptor.
+ * Prepare for a VPN application. This method is designed to solve
+ * race conditions. It first compares the current prepared package
+ * with {@code oldPackage}. If they are the same, the prepared
+ * package is revoked and replaced with {@code newPackage}. If
+ * {@code oldPackage} is {@code null}, the comparison is omitted.
+ * If {@code newPackage} is the same package or {@code null}, the
+ * revocation is omitted. This method returns {@code true} if the
+ * operation is succeeded.
*
- * @param config The parameters to configure the interface.
- * @return The file descriptor of the interface.
+ * Legacy VPN is handled specially since it is not a real package.
+ * It uses {@link VpnConfig#LEGACY_VPN} as its package name, and
+ * it can be revoked by itself.
+ *
+ * @param oldPackage The package name of the old VPN application.
+ * @param newPackage The package name of the new VPN application.
+ * @return true if the operation is succeeded.
+ */
+ public synchronized boolean prepare(String oldPackage, String newPackage) {
+ // Return false if the package does not match.
+ if (oldPackage != null && !oldPackage.equals(mPackage)) {
+ return false;
+ }
+
+ // Return true if we do not need to revoke.
+ if (newPackage == null ||
+ (newPackage.equals(mPackage) && !newPackage.equals(VpnConfig.LEGACY_VPN))) {
+ return true;
+ }
+
+ // Only system user can revoke a package.
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Unauthorized Caller");
+ }
+
+ // Check the permission of the given package.
+ PackageManager pm = mContext.getPackageManager();
+ if (!newPackage.equals(VpnConfig.LEGACY_VPN) &&
+ pm.checkPermission(VPN, newPackage) != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(newPackage + " does not have " + VPN);
+ }
+
+ // Reset the interface and hide the notification.
+ if (mInterface != null) {
+ jniReset(mInterface);
+ mCallback.restore();
+ hideNotification();
+ mInterface = null;
+ }
+
+ // Send out the broadcast or stop LegacyVpnRunner.
+ if (!mPackage.equals(VpnConfig.LEGACY_VPN)) {
+ Intent intent = new Intent(VpnConfig.ACTION_VPN_REVOKED);
+ intent.setPackage(mPackage);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mContext.sendBroadcast(intent);
+ } else if (mLegacyVpnRunner != null) {
+ mLegacyVpnRunner.exit();
+ mLegacyVpnRunner = null;
+ }
+
+ Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
+ mPackage = newPackage;
+ return true;
+ }
+
+ /**
+ * Establish a VPN network and return the file descriptor of the VPN
+ * interface. This methods returns {@code null} if the application is
+ * revoked or not prepared.
+ *
+ * @param config The parameters to configure the network.
+ * @return The file descriptor of the VPN interface.
*/
public synchronized ParcelFileDescriptor establish(VpnConfig config) {
// Check the permission of the caller.
@@ -151,7 +167,7 @@
PackageManager pm = mContext.getPackageManager();
ApplicationInfo app = null;
try {
- app = pm.getApplicationInfo(mPackageName, 0);
+ app = pm.getApplicationInfo(mPackage, 0);
} catch (Exception e) {
return null;
}
@@ -175,24 +191,18 @@
icon.draw(new Canvas(bitmap));
}
- // Create the interface and abort if any of the following steps fails.
- ParcelFileDescriptor descriptor =
- ParcelFileDescriptor.adoptFd(jniCreateInterface(config.mtu));
+ // Configure the interface. Abort if any of these steps fails.
+ ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(
+ jniConfigure(config.mtu, config.addresses, config.routes));
try {
- String name = jniGetInterfaceName(descriptor.getFd());
- if (jniSetAddresses(name, config.addresses) < 1) {
- throw new IllegalArgumentException("At least one address must be specified");
+ String interfaze = jniGetName(tun.getFd());
+ if (mInterface != null && !mInterface.equals(interfaze)) {
+ jniReset(mInterface);
}
- if (config.routes != null) {
- jniSetRoutes(name, config.routes);
- }
- if (mInterfaceName != null && !mInterfaceName.equals(name)) {
- jniResetInterface(mInterfaceName);
- }
- mInterfaceName = name;
+ mInterface = interfaze;
} catch (RuntimeException e) {
try {
- descriptor.close();
+ tun.close();
} catch (Exception ex) {
// ignore
}
@@ -203,32 +213,32 @@
mCallback.override(config.dnsServers, config.searchDomains);
// Fill more values.
- config.packageName = mPackageName;
- config.interfaceName = mInterfaceName;
+ config.packagz = mPackage;
+ config.interfaze = mInterface;
// Show the notification!
showNotification(config, label, bitmap);
- return descriptor;
+ return tun;
}
// INetworkManagementEventObserver.Stub
- public void interfaceStatusChanged(String name, boolean up) {
+ public void interfaceStatusChanged(String interfaze, boolean up) {
}
// INetworkManagementEventObserver.Stub
- public void interfaceLinkStateChanged(String name, boolean up) {
+ public void interfaceLinkStateChanged(String interfaze, boolean up) {
}
// INetworkManagementEventObserver.Stub
- public void interfaceAdded(String name) {
+ public void interfaceAdded(String interfaze) {
}
// INetworkManagementEventObserver.Stub
- public synchronized void interfaceRemoved(String name) {
- if (name.equals(mInterfaceName) && jniCheckInterface(name) == 0) {
- hideNotification();
+ public synchronized void interfaceRemoved(String interfaze) {
+ if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
mCallback.restore();
- mInterfaceName = null;
+ hideNotification();
+ mInterface = null;
}
}
@@ -239,8 +249,9 @@
if (nm != null) {
String title = (label == null) ? mContext.getString(R.string.vpn_title) :
mContext.getString(R.string.vpn_title_long, label);
- String text = (config.sessionName == null) ? mContext.getString(R.string.vpn_text) :
- mContext.getString(R.string.vpn_text_long, config.sessionName);
+ String text = (config.session == null) ? mContext.getString(R.string.vpn_text) :
+ mContext.getString(R.string.vpn_text_long, config.session);
+ config.startTime = SystemClock.elapsedRealtime();
long identity = Binder.clearCallingIdentity();
Notification notification = new Notification.Builder(mContext)
@@ -248,7 +259,7 @@
.setLargeIcon(icon)
.setContentTitle(title)
.setContentText(text)
- .setContentIntent(VpnConfig.getIntentForNotification(mContext, config))
+ .setContentIntent(VpnConfig.getIntentForStatusPanel(mContext, config))
.setDefaults(Notification.DEFAULT_ALL)
.setOngoing(true)
.getNotification();
@@ -268,35 +279,42 @@
}
}
- private native int jniCreateInterface(int mtu);
- private native String jniGetInterfaceName(int fd);
- private native int jniSetAddresses(String name, String addresses);
- private native int jniSetRoutes(String name, String routes);
- private native void jniResetInterface(String name);
- private native int jniCheckInterface(String name);
- private native void jniProtectSocket(int fd, String name);
+ private native int jniConfigure(int mtu, String addresses, String routes);
+ private native String jniGetName(int tun);
+ private native void jniReset(String interfaze);
+ private native int jniCheck(String interfaze);
+ private native void jniProtect(int socket, String interfaze);
/**
- * Handle legacy VPN requests. This method stops the daemons and restart
- * them if their arguments are not null. Heavy things are offloaded to
- * another thread, so callers will not be blocked too long.
+ * Start legacy VPN. This method stops the daemons and restart them
+ * if arguments are not null. Heavy things are offloaded to another
+ * thread, so callers will not be blocked for a long time.
*
+ * @param config The parameters to configure the network.
* @param raoocn The arguments to be passed to racoon.
* @param mtpd The arguments to be passed to mtpd.
*/
public synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) {
- // Stop the current VPN just like a normal VPN application.
- prepare("");
+ // Prepare for the new request. This also checks the caller.
+ prepare(null, VpnConfig.LEGACY_VPN);
- // Legacy VPN does not have a package name.
- config.packageName = null;
-
- // Start a new runner and we are done!
+ // Start a new LegacyVpnRunner and we are done!
mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd);
mLegacyVpnRunner.start();
}
/**
+ * Return the information of the current ongoing legacy VPN.
+ */
+ public synchronized LegacyVpnInfo getLegacyVpnInfo() {
+ // Only system user can call this method.
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Unauthorized Caller");
+ }
+ return (mLegacyVpnRunner == null) ? null : mLegacyVpnRunner.getInfo();
+ }
+
+ /**
* Bringing up a VPN connection takes time, and that is all this thread
* does. Here we have plenty of time. The only thing we need to take
* care of is responding to interruptions as soon as possible. Otherwise
@@ -310,6 +328,8 @@
private final VpnConfig mConfig;
private final String[] mDaemons;
private final String[][] mArguments;
+ private final LegacyVpnInfo mInfo;
+
private long mTimer = -1;
public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) {
@@ -317,23 +337,37 @@
mConfig = config;
mDaemons = new String[] {"racoon", "mtpd"};
mArguments = new String[][] {racoon, mtpd};
+ mInfo = new LegacyVpnInfo();
+
+ // Legacy VPN is not a real package, so we use it to carry the key.
+ mInfo.key = mConfig.packagz;
+ mConfig.packagz = VpnConfig.LEGACY_VPN;
}
public void exit() {
+ // We assume that everything is reset after the daemons die.
for (String daemon : mDaemons) {
SystemProperties.set("ctl.stop", daemon);
}
interrupt();
}
+ public LegacyVpnInfo getInfo() {
+ // Update the info when VPN is disconnected.
+ if (mInfo.state == LegacyVpnInfo.STATE_CONNECTED && mInterface == null) {
+ mInfo.state = LegacyVpnInfo.STATE_DISCONNECTED;
+ mInfo.intent = null;
+ }
+ return mInfo;
+ }
+
@Override
public void run() {
// Wait for the previous thread since it has been interrupted.
- Log.v(TAG, "wait");
+ Log.v(TAG, "Waiting");
synchronized (TAG) {
- Log.v(TAG, "begin");
+ Log.v(TAG, "Executing");
execute();
- Log.v(TAG, "end");
}
}
@@ -345,7 +379,8 @@
} else if (now - mTimer <= 30000) {
Thread.sleep(yield ? 200 : 1);
} else {
- throw new InterruptedException("timeout");
+ mInfo.state = LegacyVpnInfo.STATE_TIMEOUT;
+ throw new IllegalStateException("time is up");
}
}
@@ -354,6 +389,7 @@
try {
// Initialize the timer.
checkpoint(false);
+ mInfo.state = LegacyVpnInfo.STATE_INITIALIZING;
// First stop the daemons.
for (String daemon : mDaemons) {
@@ -376,14 +412,16 @@
checkpoint(true);
}
- // Check if we need to restart some daemons.
+ // Check if we need to restart any of the daemons.
boolean restart = false;
for (String[] arguments : mArguments) {
restart = restart || (arguments != null);
}
if (!restart) {
+ mInfo.state = LegacyVpnInfo.STATE_DISCONNECTED;
return;
}
+ mInfo.state = LegacyVpnInfo.STATE_CONNECTING;
// Start the daemon with arguments.
for (int i = 0; i < mDaemons.length; ++i) {
@@ -424,7 +462,7 @@
for (String argument : arguments) {
byte[] bytes = argument.getBytes(Charsets.UTF_8);
if (bytes.length >= 0xFFFF) {
- throw new IllegalArgumentException("argument too large");
+ throw new IllegalArgumentException("argument is too large");
}
out.write(bytes.length >> 8);
out.write(bytes.length);
@@ -451,14 +489,14 @@
String daemon = mDaemons[i];
if (mArguments[i] != null && !"running".equals(
SystemProperties.get("init.svc." + daemon))) {
- throw new IllegalArgumentException(daemon + " is dead");
+ throw new IllegalStateException(daemon + " is dead");
}
}
checkpoint(true);
}
// Now we are connected. Get the interface.
- mConfig.interfaceName = SystemProperties.get("vpn.via");
+ mConfig.interfaze = SystemProperties.get("vpn.via");
// Get the DNS servers if they are not set in config.
if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
@@ -468,25 +506,35 @@
}
}
- // TODO: support routes and search domains for IPSec Mode-CFG.
+ // TODO: support search domains from ISAKMP mode config.
- // This is it! Here is the end of our journey!
+ // The final step must be synchronized.
synchronized (Vpn.this) {
// Check if the thread is interrupted while we are waiting.
checkpoint(false);
- if (mConfig.routes != null) {
- jniSetRoutes(mConfig.interfaceName, mConfig.routes);
+ // Check if the interface is gone while we are waiting.
+ if (jniCheck(mConfig.interfaze) == 0) {
+ throw new IllegalStateException(mConfig.interfaze + " is gone");
}
- mInterfaceName = mConfig.interfaceName;
+
+ // Now INetworkManagementEventObserver is watching our back.
+ mInterface = mConfig.interfaze;
mCallback.override(mConfig.dnsServers, mConfig.searchDomains);
showNotification(mConfig, null, null);
+
+ Log.i(TAG, "Connected!");
+ mInfo.state = LegacyVpnInfo.STATE_CONNECTED;
+ mInfo.intent = VpnConfig.getIntentForStatusPanel(mContext, null);
}
- Log.i(TAG, "Connected!");
} catch (Exception e) {
- Log.i(TAG, "Abort due to " + e.getMessage());
- for (String daemon : mDaemons) {
- SystemProperties.set("ctl.stop", daemon);
+ Log.i(TAG, "Aborting", e);
+ exit();
+ } finally {
+ // Do not leave an unstable state.
+ if (mInfo.state == LegacyVpnInfo.STATE_INITIALIZING ||
+ mInfo.state == LegacyVpnInfo.STATE_CONNECTING) {
+ mInfo.state = LegacyVpnInfo.STATE_FAILED;
}
}
}
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index 13a76b3..c157cf1 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -530,11 +530,12 @@
notification.sound = null;
notification.vibrate = null;
- Intent intent = new Intent();
+ Intent intent = new Intent(
+ Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- intent.setClassName("com.android.systemui",
- "com.android.systemui.usb.UsbPreferenceActivity");
+ intent.setComponent(new ComponentName("com.android.settings",
+ "com.android.settings.UsbSettings"));
PendingIntent pi = PendingIntent.getActivity(mContext, 0,
intent, 0);
notification.setLatestEventInfo(mContext, title, message, pi);
diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java
index ee69311..65007f9 100644
--- a/services/java/com/android/server/wm/InputManager.java
+++ b/services/java/com/android/server/wm/InputManager.java
@@ -551,18 +551,6 @@
android.Manifest.permission.INJECT_EVENTS, injectorPid, injectorUid)
== PackageManager.PERMISSION_GRANTED;
}
-
- @SuppressWarnings("unused")
- public boolean filterTouchEvents() {
- return mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_filterTouchEvents);
- }
-
- @SuppressWarnings("unused")
- public boolean filterJumpyTouchEvents() {
- return mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_filterJumpyTouchEvents);
- }
@SuppressWarnings("unused")
public int getVirtualKeyQuietTimeMillis() {
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 8a46ab0..14a4109 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -69,8 +69,6 @@
jmethodID interceptKeyBeforeDispatching;
jmethodID dispatchUnhandledKey;
jmethodID checkInjectEventsPermission;
- jmethodID filterTouchEvents;
- jmethodID filterJumpyTouchEvents;
jmethodID getVirtualKeyQuietTimeMillis;
jmethodID getExcludedDeviceNames;
jmethodID getKeyRepeatTimeout;
@@ -381,18 +379,6 @@
void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outConfig) {
JNIEnv* env = jniEnv();
- jboolean filterTouchEvents = env->CallBooleanMethod(mCallbacksObj,
- gCallbacksClassInfo.filterTouchEvents);
- if (!checkAndClearExceptionFromCallback(env, "filterTouchEvents")) {
- outConfig->filterTouchEvents = filterTouchEvents;
- }
-
- jboolean filterJumpyTouchEvents = env->CallBooleanMethod(mCallbacksObj,
- gCallbacksClassInfo.filterJumpyTouchEvents);
- if (!checkAndClearExceptionFromCallback(env, "filterJumpyTouchEvents")) {
- outConfig->filterJumpyTouchEvents = filterJumpyTouchEvents;
- }
-
jint virtualKeyQuietTime = env->CallIntMethod(mCallbacksObj,
gCallbacksClassInfo.getVirtualKeyQuietTimeMillis);
if (!checkAndClearExceptionFromCallback(env, "getVirtualKeyQuietTimeMillis")) {
@@ -1405,12 +1391,6 @@
GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, clazz,
"checkInjectEventsPermission", "(II)Z");
- GET_METHOD_ID(gCallbacksClassInfo.filterTouchEvents, clazz,
- "filterTouchEvents", "()Z");
-
- GET_METHOD_ID(gCallbacksClassInfo.filterJumpyTouchEvents, clazz,
- "filterJumpyTouchEvents", "()Z");
-
GET_METHOD_ID(gCallbacksClassInfo.getVirtualKeyQuietTimeMillis, clazz,
"getVirtualKeyQuietTimeMillis", "()I");
diff --git a/services/jni/com_android_server_connectivity_Vpn.cpp b/services/jni/com_android_server_connectivity_Vpn.cpp
index 62d7636..5f920f1 100644
--- a/services/jni/com_android_server_connectivity_Vpn.cpp
+++ b/services/jni/com_android_server_connectivity_Vpn.cpp
@@ -54,7 +54,7 @@
#define SYSTEM_ERROR -1
#define BAD_ARGUMENT -2
-static int create_interface(int mtu)
+static int create_interface(char *name, int *index, int mtu)
{
int tun = open("/dev/tun", O_RDWR | O_NONBLOCK);
@@ -82,6 +82,14 @@
goto error;
}
+ // Get interface index.
+ if (ioctl(inet4, SIOGIFINDEX, &ifr4)) {
+ LOGE("Cannot get index of %s: %s", ifr4.ifr_name, strerror(errno));
+ goto error;
+ }
+
+ strncpy(name, ifr4.ifr_name, IFNAMSIZ);
+ *index = ifr4.ifr_ifindex;
return tun;
error:
@@ -89,36 +97,9 @@
return SYSTEM_ERROR;
}
-static int get_interface_name(char *name, int tun)
+static int set_addresses(const char *name, int index, const char *addresses)
{
ifreq ifr4;
- if (ioctl(tun, TUNGETIFF, &ifr4)) {
- LOGE("Cannot get interface name: %s", strerror(errno));
- return SYSTEM_ERROR;
- }
- strncpy(name, ifr4.ifr_name, IFNAMSIZ);
- return 0;
-}
-
-static int get_interface_index(const char *name)
-{
- ifreq ifr4;
- strncpy(ifr4.ifr_name, name, IFNAMSIZ);
- if (ioctl(inet4, SIOGIFINDEX, &ifr4)) {
- LOGE("Cannot get index of %s: %s", name, strerror(errno));
- return SYSTEM_ERROR;
- }
- return ifr4.ifr_ifindex;
-}
-
-static int set_addresses(const char *name, const char *addresses)
-{
- int index = get_interface_index(name);
- if (index < 0) {
- return index;
- }
-
- ifreq ifr4;
memset(&ifr4, 0, sizeof(ifr4));
strncpy(ifr4.ifr_name, name, IFNAMSIZ);
ifr4.ifr_addr.sa_family = AF_INET;
@@ -187,13 +168,8 @@
return count;
}
-static int set_routes(const char *name, const char *routes)
+static int set_routes(const char *name, int index, const char *routes)
{
- int index = get_interface_index(name);
- if (index < 0) {
- return index;
- }
-
rtentry rt4;
memset(&rt4, 0, sizeof(rt4));
rt4.rt_dev = (char *)name;
@@ -277,6 +253,17 @@
return count;
}
+static int get_interface_name(char *name, int tun)
+{
+ ifreq ifr4;
+ if (ioctl(tun, TUNGETIFF, &ifr4)) {
+ LOGE("Cannot get interface name: %s", strerror(errno));
+ return SYSTEM_ERROR;
+ }
+ strncpy(name, ifr4.ifr_name, IFNAMSIZ);
+ return 0;
+}
+
static int reset_interface(const char *name)
{
ifreq ifr4;
@@ -302,9 +289,9 @@
return ifr4.ifr_flags;
}
-static int bind_to_interface(int fd, const char *name)
+static int bind_to_interface(int socket, const char *name)
{
- if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name))) {
+ if (setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name))) {
LOGE("Cannot bind socket to %s: %s", name, strerror(errno));
return SYSTEM_ERROR;
}
@@ -322,17 +309,56 @@
}
}
-static jint createInterface(JNIEnv *env, jobject thiz, jint mtu)
+static jint configure(JNIEnv *env, jobject thiz,
+ jint mtu, jstring jAddresses, jstring jRoutes)
{
- int tun = create_interface(mtu);
+ char name[IFNAMSIZ];
+ int index;
+ int tun = create_interface(name, &index, mtu);
if (tun < 0) {
throwException(env, tun, "Cannot create interface");
return -1;
}
+
+ const char *addresses = NULL;
+ const char *routes = NULL;
+ int count;
+
+ // At least one address must be set.
+ addresses = jAddresses ? env->GetStringUTFChars(jAddresses, NULL) : NULL;
+ if (!addresses) {
+ jniThrowNullPointerException(env, "address");
+ goto error;
+ }
+ count = set_addresses(name, index, addresses);
+ env->ReleaseStringUTFChars(jAddresses, addresses);
+ if (count <= 0) {
+ throwException(env, count, "Cannot set address");
+ goto error;
+ }
+ LOGD("Configured %d address(es) on %s", count, name);
+
+ // On the contrary, routes are optional.
+ routes = jRoutes ? env->GetStringUTFChars(jRoutes, NULL) : NULL;
+ if (routes) {
+ count = set_routes(name, index, routes);
+ env->ReleaseStringUTFChars(jRoutes, routes);
+ if (count < 0) {
+ throwException(env, count, "Cannot set route");
+ goto error;
+ }
+ LOGD("Configured %d route(s) on %s", count, name);
+ }
+
return tun;
+
+error:
+ close(tun);
+ LOGD("%s is destroyed", name);
+ return -1;
}
-static jstring getInterfaceName(JNIEnv *env, jobject thiz, jint tun)
+static jstring getName(JNIEnv *env, jobject thiz, jint tun)
{
char name[IFNAMSIZ];
if (get_interface_name(name, tun) < 0) {
@@ -342,73 +368,7 @@
return env->NewStringUTF(name);
}
-static jint setAddresses(JNIEnv *env, jobject thiz, jstring jName,
- jstring jAddresses)
-{
- const char *name = NULL;
- const char *addresses = NULL;
- int count = -1;
-
- name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
- if (!name) {
- jniThrowNullPointerException(env, "name");
- goto error;
- }
- addresses = jAddresses ? env->GetStringUTFChars(jAddresses, NULL) : NULL;
- if (!addresses) {
- jniThrowNullPointerException(env, "addresses");
- goto error;
- }
- count = set_addresses(name, addresses);
- if (count < 0) {
- throwException(env, count, "Cannot set address");
- count = -1;
- }
-
-error:
- if (name) {
- env->ReleaseStringUTFChars(jName, name);
- }
- if (addresses) {
- env->ReleaseStringUTFChars(jAddresses, addresses);
- }
- return count;
-}
-
-static jint setRoutes(JNIEnv *env, jobject thiz, jstring jName,
- jstring jRoutes)
-{
- const char *name = NULL;
- const char *routes = NULL;
- int count = -1;
-
- name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
- if (!name) {
- jniThrowNullPointerException(env, "name");
- goto error;
- }
- routes = jRoutes ? env->GetStringUTFChars(jRoutes, NULL) : NULL;
- if (!routes) {
- jniThrowNullPointerException(env, "routes");
- goto error;
- }
- count = set_routes(name, routes);
- if (count < 0) {
- throwException(env, count, "Cannot set route");
- count = -1;
- }
-
-error:
- if (name) {
- env->ReleaseStringUTFChars(jName, name);
- }
- if (routes) {
- env->ReleaseStringUTFChars(jRoutes, routes);
- }
- return count;
-}
-
-static void resetInterface(JNIEnv *env, jobject thiz, jstring jName)
+static void reset(JNIEnv *env, jobject thiz, jstring jName)
{
const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
if (!name) {
@@ -421,7 +381,7 @@
env->ReleaseStringUTFChars(jName, name);
}
-static jint checkInterface(JNIEnv *env, jobject thiz, jstring jName)
+static jint check(JNIEnv *env, jobject thiz, jstring jName)
{
const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
if (!name) {
@@ -433,14 +393,14 @@
return flags;
}
-static void protectSocket(JNIEnv *env, jobject thiz, jint fd, jstring jName)
+static void protect(JNIEnv *env, jobject thiz, jint socket, jstring jName)
{
const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL;
if (!name) {
jniThrowNullPointerException(env, "name");
return;
}
- if (bind_to_interface(fd, name) < 0) {
+ if (bind_to_interface(socket, name) < 0) {
throwException(env, SYSTEM_ERROR, "Cannot protect socket");
}
env->ReleaseStringUTFChars(jName, name);
@@ -449,13 +409,11 @@
//------------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
- {"jniCreateInterface", "(I)I", (void *)createInterface},
- {"jniGetInterfaceName", "(I)Ljava/lang/String;", (void *)getInterfaceName},
- {"jniSetAddresses", "(Ljava/lang/String;Ljava/lang/String;)I", (void *)setAddresses},
- {"jniSetRoutes", "(Ljava/lang/String;Ljava/lang/String;)I", (void *)setRoutes},
- {"jniResetInterface", "(Ljava/lang/String;)V", (void *)resetInterface},
- {"jniCheckInterface", "(Ljava/lang/String;)I", (void *)checkInterface},
- {"jniProtectSocket", "(ILjava/lang/String;)V", (void *)protectSocket},
+ {"jniConfigure", "(ILjava/lang/String;Ljava/lang/String;)I", (void *)configure},
+ {"jniGetName", "(I)Ljava/lang/String;", (void *)getName},
+ {"jniReset", "(Ljava/lang/String;)V", (void *)reset},
+ {"jniCheck", "(Ljava/lang/String;)I", (void *)check},
+ {"jniProtect", "(ILjava/lang/String;)V", (void *)protect},
};
int register_android_server_connectivity_Vpn(JNIEnv *env)
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index a774841..33125c4 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -103,6 +103,11 @@
{
mNativeWindow = new FramebufferNativeWindow();
framebuffer_device_t const * fbDev = mNativeWindow->getDevice();
+ if (!fbDev) {
+ LOGE("Display subsystem failed to initialize. check logs. exiting...");
+ exit(0);
+ }
+
mDpiX = mNativeWindow->xdpi;
mDpiY = mNativeWindow->ydpi;
mRefreshRate = fbDev->fps;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 685613e..cccab4a 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -163,9 +163,34 @@
const nsecs_t duration = now - mBootTime;
LOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
mBootFinished = true;
+
+ // wait patiently for the window manager death
+ const String16 name("window");
+ sp<IBinder> window(defaultServiceManager()->getService(name));
+ if (window != 0) {
+ window->linkToDeath(this);
+ }
+
+ // stop boot animation
property_set("ctl.stop", "bootanim");
}
+void SurfaceFlinger::binderDied(const wp<IBinder>& who)
+{
+ // the window manager died on us. prepare its eulogy.
+
+ // unfreeze the screen in case it was... frozen
+ mFreezeDisplayTime = 0;
+ mFreezeCount = 0;
+ mFreezeDisplay = false;
+
+ // reset screen orientation
+ setOrientation(0, eOrientationDefault, 0);
+
+ // restart the boot-animation
+ property_set("ctl.start", "bootanim");
+}
+
void SurfaceFlinger::onFirstRef()
{
run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY);
@@ -2470,11 +2495,14 @@
GraphicBufferAlloc::~GraphicBufferAlloc() {}
sp<GraphicBuffer> GraphicBufferAlloc::createGraphicBuffer(uint32_t w, uint32_t h,
- PixelFormat format, uint32_t usage) {
+ PixelFormat format, uint32_t usage, status_t* error) {
sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(w, h, format, usage));
status_t err = graphicBuffer->initCheck();
+ *error = err;
if (err != 0 || graphicBuffer->handle == 0) {
- GraphicBuffer::dumpAllocationsToSystemLog();
+ if (err == NO_MEMORY) {
+ GraphicBuffer::dumpAllocationsToSystemLog();
+ }
LOGE("GraphicBufferAlloc::createGraphicBuffer(w=%d, h=%d) "
"failed (%s), handle=%p",
w, h, strerror(-err), graphicBuffer->handle);
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index b49fa36..15661f0 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -96,7 +96,7 @@
GraphicBufferAlloc();
virtual ~GraphicBufferAlloc();
virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h,
- PixelFormat format, uint32_t usage);
+ PixelFormat format, uint32_t usage, status_t* error);
};
// ---------------------------------------------------------------------------
@@ -147,6 +147,7 @@
class SurfaceFlinger :
public BinderService<SurfaceFlinger>,
public BnSurfaceComposer,
+ public IBinder::DeathRecipient,
protected Thread
{
public:
@@ -193,6 +194,10 @@
sp<Layer> getLayer(const sp<ISurface>& sur) const;
private:
+ // DeathRecipient interface
+ virtual void binderDied(const wp<IBinder>& who);
+
+private:
friend class Client;
friend class LayerBase;
friend class LayerBaseClient;
diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java
index 47863bd..c553947 100644
--- a/voip/java/com/android/server/sip/SipService.java
+++ b/voip/java/com/android/server/sip/SipService.java
@@ -74,6 +74,7 @@
private static final int SHORT_EXPIRY_TIME = 10;
private static final int MIN_EXPIRY_TIME = 60;
private static final int DEFAULT_KEEPALIVE_INTERVAL = 10; // in seconds
+ private static final int DEFAULT_MAX_KEEPALIVE_INTERVAL = 120; // in seconds
private Context mContext;
private String mLocalIp;
@@ -83,6 +84,8 @@
private WifiScanProcess mWifiScanProcess;
private WifiManager.WifiLock mWifiLock;
private boolean mWifiOnly;
+ private BroadcastReceiver mWifiStateReceiver = null;
+
private IntervalMeasurementProcess mIntervalMeasurementProcess;
private MyExecutor mExecutor = new MyExecutor();
@@ -99,6 +102,7 @@
private boolean mWifiEnabled;
private SipWakeLock mMyWakeLock;
private int mKeepAliveInterval;
+ private int mLastGoodKeepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL;
/**
* Starts the SIP service. Do nothing if the SIP API is not supported on the
@@ -123,40 +127,47 @@
mWifiOnly = SipManager.isSipWifiOnly(context);
}
- private BroadcastReceiver mWifiStateReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
- int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
- WifiManager.WIFI_STATE_UNKNOWN);
- synchronized (SipService.this) {
- switch (state) {
- case WifiManager.WIFI_STATE_ENABLED:
- mWifiEnabled = true;
- if (anyOpenedToReceiveCalls()) grabWifiLock();
- break;
- case WifiManager.WIFI_STATE_DISABLED:
- mWifiEnabled = false;
- releaseWifiLock();
- break;
+ private BroadcastReceiver createWifiBroadcastReceiver() {
+ return new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
+ int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+ WifiManager.WIFI_STATE_UNKNOWN);
+ synchronized (SipService.this) {
+ switch (state) {
+ case WifiManager.WIFI_STATE_ENABLED:
+ mWifiEnabled = true;
+ if (anyOpenedToReceiveCalls()) grabWifiLock();
+ break;
+ case WifiManager.WIFI_STATE_DISABLED:
+ mWifiEnabled = false;
+ releaseWifiLock();
+ break;
+ }
}
}
}
- }
+ };
};
private void registerReceivers() {
mContext.registerReceiver(mConnectivityReceiver,
new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
- mContext.registerReceiver(mWifiStateReceiver,
- new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION));
+ if (SipManager.isSipWifiOnly(mContext)) {
+ mWifiStateReceiver = createWifiBroadcastReceiver();
+ mContext.registerReceiver(mWifiStateReceiver,
+ new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION));
+ }
if (DEBUG) Log.d(TAG, " +++ register receivers");
}
private void unregisterReceivers() {
mContext.unregisterReceiver(mConnectivityReceiver);
- mContext.unregisterReceiver(mWifiStateReceiver);
+ if (SipManager.isSipWifiOnly(mContext)) {
+ mContext.unregisterReceiver(mWifiStateReceiver);
+ }
if (DEBUG) Log.d(TAG, " --- unregister receivers");
}
@@ -439,6 +450,7 @@
if (connected) {
mLocalIp = determineLocalIp();
mKeepAliveInterval = -1;
+ mLastGoodKeepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL;
for (SipSessionGroupExt group : mSipGroups.values()) {
group.onConnectivityChanged(true);
}
@@ -462,7 +474,8 @@
private void startPortMappingLifetimeMeasurement(
SipProfile localProfile) {
- startPortMappingLifetimeMeasurement(localProfile, -1);
+ startPortMappingLifetimeMeasurement(localProfile,
+ DEFAULT_MAX_KEEPALIVE_INTERVAL);
}
private void startPortMappingLifetimeMeasurement(
@@ -473,8 +486,16 @@
Log.d(TAG, "start NAT port mapping timeout measurement on "
+ localProfile.getUriString());
- mIntervalMeasurementProcess =
- new IntervalMeasurementProcess(localProfile, maxInterval);
+ int minInterval = mLastGoodKeepAliveInterval;
+ if (minInterval >= maxInterval) {
+ // If mLastGoodKeepAliveInterval also does not work, reset it
+ // to the default min
+ minInterval = mLastGoodKeepAliveInterval
+ = DEFAULT_KEEPALIVE_INTERVAL;
+ Log.d(TAG, " reset min interval to " + minInterval);
+ }
+ mIntervalMeasurementProcess = new IntervalMeasurementProcess(
+ localProfile, minInterval, maxInterval);
mIntervalMeasurementProcess.start();
}
}
@@ -530,7 +551,7 @@
private int getKeepAliveInterval() {
return (mKeepAliveInterval < 0)
- ? DEFAULT_KEEPALIVE_INTERVAL
+ ? mLastGoodKeepAliveInterval
: mKeepAliveInterval;
}
@@ -759,27 +780,33 @@
private class IntervalMeasurementProcess implements Runnable,
SipSessionGroup.KeepAliveProcessCallback {
private static final String TAG = "SipKeepAliveInterval";
- private static final int MAX_INTERVAL = 120; // in seconds
private static final int MIN_INTERVAL = 5; // in seconds
private static final int PASS_THRESHOLD = 10;
private static final int MAX_RETRY_COUNT = 5;
private static final int NAT_MEASUREMENT_RETRY_INTERVAL = 120; // in seconds
private SipSessionGroupExt mGroup;
private SipSessionGroup.SipSessionImpl mSession;
- private int mMinInterval = DEFAULT_KEEPALIVE_INTERVAL; // in seconds
+ private int mMinInterval;
private int mMaxInterval;
private int mInterval;
private int mPassCount = 0;
- public IntervalMeasurementProcess(SipProfile localProfile, int maxInterval) {
- mMaxInterval = (maxInterval < 0) ? MAX_INTERVAL : maxInterval;
- mInterval = (mMaxInterval + mMinInterval) / 2;
+ public IntervalMeasurementProcess(SipProfile localProfile,
+ int minInterval, int maxInterval) {
+ mMaxInterval = maxInterval;
+ mMinInterval = minInterval;
+ mInterval = (maxInterval + minInterval) / 2;
// Don't start measurement if the interval is too small
- if (mInterval < MIN_INTERVAL) {
+ if (mInterval < DEFAULT_KEEPALIVE_INTERVAL) {
Log.w(TAG, "interval is too small; measurement aborted; "
+ "maxInterval=" + mMaxInterval);
return;
+ } else if (checkTermination()) {
+ Log.w(TAG, "interval is too small; measurement aborted; "
+ + "interval=[" + mMinInterval + "," + mMaxInterval
+ + "]");
+ return;
}
try {
@@ -833,6 +860,10 @@
}
}
+ private boolean checkTermination() {
+ return ((mMaxInterval - mMinInterval) < MIN_INTERVAL);
+ }
+
// SipSessionGroup.KeepAliveProcessCallback
@Override
public void onResponse(boolean portChanged) {
@@ -841,6 +872,9 @@
if (++mPassCount != PASS_THRESHOLD) return;
// update the interval, since the current interval is good to
// keep the port mapping.
+ if (mKeepAliveInterval > 0) {
+ mLastGoodKeepAliveInterval = mKeepAliveInterval;
+ }
mKeepAliveInterval = mMinInterval = mInterval;
if (DEBUG) {
Log.d(TAG, "measured good keepalive interval: "
@@ -851,9 +885,13 @@
// Since the rport is changed, shorten the interval.
mMaxInterval = mInterval;
}
- if ((mMaxInterval - mMinInterval) < MIN_INTERVAL) {
+ if (checkTermination()) {
// update mKeepAliveInterval and stop measurement.
stop();
+ // If all the measurements failed, we still set it to
+ // mMinInterval; If mMinInterval still doesn't work, a new
+ // measurement with min interval=DEFAULT_KEEPALIVE_INTERVAL
+ // will be conducted.
mKeepAliveInterval = mMinInterval;
if (DEBUG) {
Log.d(TAG, "measured keepalive interval: "
diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
index 047eb8d..4e44402 100644
--- a/voip/java/com/android/server/sip/SipSessionGroup.java
+++ b/voip/java/com/android/server/sip/SipSessionGroup.java
@@ -1223,9 +1223,9 @@
private void establishCall(boolean enableKeepAlive) {
mState = SipSession.State.IN_CALL;
- mInCall = true;
cancelSessionTimer();
- if (enableKeepAlive) enableKeepAlive();
+ if (!mInCall && enableKeepAlive) enableKeepAlive();
+ mInCall = true;
mProxy.onCallEstablished(this, mPeerSessionDescription);
}
diff --git a/vpn/java/android/net/vpn/IVpnService.aidl b/vpn/java/android/net/vpn/IVpnService.aidl
deleted file mode 100644
index 6bf3edd..0000000
--- a/vpn/java/android/net/vpn/IVpnService.aidl
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2009, 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.vpn;
-
-import android.net.vpn.VpnProfile;
-
-/**
- * Interface to access a VPN service.
- * {@hide}
- */
-interface IVpnService {
- /**
- * Sets up a VPN connection.
- * @param profile the profile object
- * @param username the username for authentication
- * @param password the corresponding password for authentication
- * @return true if VPN is successfully connected
- */
- boolean connect(in VpnProfile profile, String username, String password);
-
- /**
- * Tears down the VPN connection.
- */
- void disconnect();
-
- /**
- * Gets the the current connection state.
- */
- String getState(in VpnProfile profile);
-
- /**
- * Returns the idle state.
- * @return true if the system is not connecting/connected to a VPN
- */
- boolean isIdle();
-}
diff --git a/vpn/java/android/net/vpn/L2tpIpsecProfile.java b/vpn/java/android/net/vpn/L2tpIpsecProfile.java
deleted file mode 100644
index 4ae2dec..0000000
--- a/vpn/java/android/net/vpn/L2tpIpsecProfile.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2009, 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.vpn;
-
-import android.os.Parcel;
-
-/**
- * The profile for certificate-based L2TP-over-IPSec type of VPN.
- * {@hide}
- */
-public class L2tpIpsecProfile extends L2tpProfile {
- private static final long serialVersionUID = 1L;
-
- private String mUserCertificate;
- private String mCaCertificate;
-
- @Override
- public VpnType getType() {
- return VpnType.L2TP_IPSEC;
- }
-
- public void setCaCertificate(String name) {
- mCaCertificate = name;
- }
-
- public String getCaCertificate() {
- return mCaCertificate;
- }
-
- public void setUserCertificate(String name) {
- mUserCertificate = name;
- }
-
- public String getUserCertificate() {
- return mUserCertificate;
- }
-
- @Override
- protected void readFromParcel(Parcel in) {
- super.readFromParcel(in);
- mCaCertificate = in.readString();
- mUserCertificate = in.readString();
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
- super.writeToParcel(parcel, flags);
- parcel.writeString(mCaCertificate);
- parcel.writeString(mUserCertificate);
- }
-}
diff --git a/vpn/java/android/net/vpn/L2tpIpsecPskProfile.java b/vpn/java/android/net/vpn/L2tpIpsecPskProfile.java
deleted file mode 100644
index 7a03018..0000000
--- a/vpn/java/android/net/vpn/L2tpIpsecPskProfile.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2009, 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.vpn;
-
-import android.os.Parcel;
-
-/**
- * The profile for pre-shared-key-based L2TP-over-IPSec type of VPN.
- * {@hide}
- */
-public class L2tpIpsecPskProfile extends L2tpProfile {
- private static final long serialVersionUID = 1L;
-
- private String mPresharedKey;
-
- @Override
- public VpnType getType() {
- return VpnType.L2TP_IPSEC_PSK;
- }
-
- public void setPresharedKey(String key) {
- mPresharedKey = key;
- }
-
- public String getPresharedKey() {
- return mPresharedKey;
- }
-
- @Override
- protected void readFromParcel(Parcel in) {
- super.readFromParcel(in);
- mPresharedKey = in.readString();
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
- super.writeToParcel(parcel, flags);
- parcel.writeString(mPresharedKey);
- }
-}
diff --git a/vpn/java/android/net/vpn/L2tpProfile.java b/vpn/java/android/net/vpn/L2tpProfile.java
deleted file mode 100644
index dbba0c5..0000000
--- a/vpn/java/android/net/vpn/L2tpProfile.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2009, 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.vpn;
-
-import android.os.Parcel;
-
-/**
- * The profile for L2TP type of VPN.
- * {@hide}
- */
-public class L2tpProfile extends VpnProfile {
- private static final long serialVersionUID = 1L;
-
- private boolean mSecret;
- private String mSecretString;
-
- @Override
- public VpnType getType() {
- return VpnType.L2TP;
- }
-
- /**
- * Enables/disables the secret for authenticating tunnel connection.
- */
- public void setSecretEnabled(boolean enabled) {
- mSecret = enabled;
- }
-
- public boolean isSecretEnabled() {
- return mSecret;
- }
-
- public void setSecretString(String secret) {
- mSecretString = secret;
- }
-
- public String getSecretString() {
- return mSecretString;
- }
-
- @Override
- protected void readFromParcel(Parcel in) {
- super.readFromParcel(in);
- mSecret = in.readInt() > 0;
- mSecretString = in.readString();
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
- super.writeToParcel(parcel, flags);
- parcel.writeInt(mSecret ? 1 : 0);
- parcel.writeString(mSecretString);
- }
-}
diff --git a/vpn/java/android/net/vpn/PptpProfile.java b/vpn/java/android/net/vpn/PptpProfile.java
deleted file mode 100644
index b4b7be5..0000000
--- a/vpn/java/android/net/vpn/PptpProfile.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2009, 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.vpn;
-
-import android.os.Parcel;
-
-/**
- * The profile for PPTP type of VPN.
- * {@hide}
- */
-public class PptpProfile extends VpnProfile {
- private static final long serialVersionUID = 1L;
- private boolean mEncryption = true;
-
- @Override
- public VpnType getType() {
- return VpnType.PPTP;
- }
-
- /**
- * Enables/disables the encryption for PPTP tunnel.
- */
- public void setEncryptionEnabled(boolean enabled) {
- mEncryption = enabled;
- }
-
- public boolean isEncryptionEnabled() {
- return mEncryption;
- }
-
- @Override
- protected void readFromParcel(Parcel in) {
- super.readFromParcel(in);
- mEncryption = in.readInt() > 0;
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
- super.writeToParcel(parcel, flags);
- parcel.writeInt(mEncryption ? 1 : 0);
- }
-}
diff --git a/vpn/java/android/net/vpn/VpnManager.java b/vpn/java/android/net/vpn/VpnManager.java
deleted file mode 100644
index 02486bb..0000000
--- a/vpn/java/android/net/vpn/VpnManager.java
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * Copyright (C) 2009, 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.vpn;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Environment;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemProperties;
-import android.util.Log;
-
-import com.android.server.vpn.VpnServiceBinder;
-
-/**
- * The class provides interface to manage all VPN-related tasks, including:
- * <ul>
- * <li>The list of supported VPN types.
- * <li>API's to start/stop the service of a particular type.
- * <li>API's to start the settings activity.
- * <li>API's to create a profile.
- * <li>API's to register/unregister a connectivity receiver and the keys to
- * access the fields in a connectivity broadcast event.
- * </ul>
- * {@hide}
- */
-public class VpnManager {
- /** Key to the profile name of a connectivity broadcast event. */
- public static final String BROADCAST_PROFILE_NAME = "profile_name";
- /** Key to the connectivity state of a connectivity broadcast event. */
- public static final String BROADCAST_CONNECTION_STATE = "connection_state";
- /** Key to the error code of a connectivity broadcast event. */
- public static final String BROADCAST_ERROR_CODE = "err";
- /** Error code to indicate an error from authentication. */
- public static final int VPN_ERROR_AUTH = 51;
- /** Error code to indicate the connection attempt failed. */
- public static final int VPN_ERROR_CONNECTION_FAILED = 101;
- /** Error code to indicate the server is not known. */
- public static final int VPN_ERROR_UNKNOWN_SERVER = 102;
- /** Error code to indicate an error from challenge response. */
- public static final int VPN_ERROR_CHALLENGE = 5;
- /** Error code to indicate an error of remote server hanging up. */
- public static final int VPN_ERROR_REMOTE_HUNG_UP = 7;
- /** Error code to indicate an error of remote PPP server hanging up. */
- public static final int VPN_ERROR_REMOTE_PPP_HUNG_UP = 48;
- /** Error code to indicate a PPP negotiation error. */
- public static final int VPN_ERROR_PPP_NEGOTIATION_FAILED = 42;
- /** Error code to indicate an error of losing connectivity. */
- public static final int VPN_ERROR_CONNECTION_LOST = 103;
- /** Largest error code used by VPN. */
- public static final int VPN_ERROR_LARGEST = 200;
- /** Error code to indicate a successful connection. */
- public static final int VPN_ERROR_NO_ERROR = 0;
-
- public static final String PROFILES_PATH = "/misc/vpn/profiles";
-
- private static final String PACKAGE_PREFIX =
- VpnManager.class.getPackage().getName() + ".";
-
- // Action for broadcasting a connectivity state.
- private static final String ACTION_VPN_CONNECTIVITY = "vpn.connectivity";
-
- private static final String VPN_SERVICE_NAME = "vpn";
-
- // Action to start VPN settings
- private static final String ACTION_VPN_SETTINGS =
- PACKAGE_PREFIX + "SETTINGS";
-
- public static final String TAG = VpnManager.class.getSimpleName();
-
- // TODO(oam): Test VPN when EFS is enabled (will do later)...
- public static String getProfilePath() {
- // This call will return the correct path if Encrypted FS is enabled or not.
- return Environment.getSecureDataDirectory().getPath() + PROFILES_PATH;
- }
-
- /**
- * Returns all supported VPN types.
- */
- public static VpnType[] getSupportedVpnTypes() {
- return VpnType.values();
- }
-
- public static void startVpnService(Context c) {
- ServiceManager.addService(VPN_SERVICE_NAME, new VpnServiceBinder(c));
- }
-
- private Context mContext;
- private IVpnService mVpnService;
-
- /**
- * Creates a manager object with the specified context.
- */
- public VpnManager(Context c) {
- mContext = c;
- createVpnServiceClient();
- }
-
- private void createVpnServiceClient() {
- IBinder b = ServiceManager.getService(VPN_SERVICE_NAME);
- mVpnService = IVpnService.Stub.asInterface(b);
- }
-
- /**
- * Sets up a VPN connection.
- * @param profile the profile object
- * @param username the username for authentication
- * @param password the corresponding password for authentication
- * @return true if VPN is successfully connected
- */
- public boolean connect(VpnProfile p, String username, String password) {
- try {
- return mVpnService.connect(p, username, password);
- } catch (RemoteException e) {
- Log.e(TAG, "connect()", e);
- return false;
- }
- }
-
- /**
- * Tears down the VPN connection.
- */
- public void disconnect() {
- try {
- mVpnService.disconnect();
- } catch (RemoteException e) {
- Log.e(TAG, "disconnect()", e);
- }
- }
-
- /**
- * Gets the the current connection state.
- */
- public VpnState getState(VpnProfile p) {
- try {
- return Enum.valueOf(VpnState.class, mVpnService.getState(p));
- } catch (RemoteException e) {
- Log.e(TAG, "getState()", e);
- return VpnState.IDLE;
- }
- }
-
- /**
- * Returns the idle state.
- * @return true if the system is not connecting/connected to a VPN
- */
- public boolean isIdle() {
- try {
- return mVpnService.isIdle();
- } catch (RemoteException e) {
- Log.e(TAG, "isIdle()", e);
- return true;
- }
- }
-
- /**
- * Creates a VPN profile of the specified type.
- *
- * @param type the VPN type
- * @return the profile object
- */
- public VpnProfile createVpnProfile(VpnType type) {
- return createVpnProfile(type, false);
- }
-
- /**
- * Creates a VPN profile of the specified type.
- *
- * @param type the VPN type
- * @param customized true if the profile is custom made
- * @return the profile object
- */
- public VpnProfile createVpnProfile(VpnType type, boolean customized) {
- try {
- VpnProfile p = (VpnProfile) type.getProfileClass().newInstance();
- p.setCustomized(customized);
- return p;
- } catch (InstantiationException e) {
- return null;
- } catch (IllegalAccessException e) {
- return null;
- }
- }
-
- /** Broadcasts the connectivity state of the specified profile. */
- public void broadcastConnectivity(String profileName, VpnState s) {
- broadcastConnectivity(profileName, s, VPN_ERROR_NO_ERROR);
- }
-
- /** Broadcasts the connectivity state with an error code. */
- public void broadcastConnectivity(String profileName, VpnState s,
- int error) {
- Intent intent = new Intent(ACTION_VPN_CONNECTIVITY);
- intent.putExtra(BROADCAST_PROFILE_NAME, profileName);
- intent.putExtra(BROADCAST_CONNECTION_STATE, s);
- if (error != VPN_ERROR_NO_ERROR) {
- intent.putExtra(BROADCAST_ERROR_CODE, error);
- }
- mContext.sendBroadcast(intent);
- }
-
- public void registerConnectivityReceiver(BroadcastReceiver r) {
- IntentFilter filter = new IntentFilter();
- filter.addAction(VpnManager.ACTION_VPN_CONNECTIVITY);
- mContext.registerReceiver(r, filter);
- }
-
- public void unregisterConnectivityReceiver(BroadcastReceiver r) {
- mContext.unregisterReceiver(r);
- }
-
- /** Starts the VPN settings activity. */
- public void startSettingsActivity() {
- Intent intent = new Intent(ACTION_VPN_SETTINGS);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(intent);
- }
-
- /** Creates an intent to start the VPN settings activity. */
- public Intent createSettingsActivityIntent() {
- Intent intent = new Intent(ACTION_VPN_SETTINGS);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- return intent;
- }
-}
diff --git a/vpn/java/android/net/vpn/VpnProfile.java b/vpn/java/android/net/vpn/VpnProfile.java
deleted file mode 100644
index bd6c809..0000000
--- a/vpn/java/android/net/vpn/VpnProfile.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2009, 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.vpn;
-
-import android.content.Context;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.io.IOException;
-import java.io.Serializable;
-
-/**
- * A VPN profile.
- * {@hide}
- */
-public abstract class VpnProfile implements Parcelable, Serializable {
- private static final long serialVersionUID = 1L;
- private String mName; // unique display name
- private String mId; // unique identifier
- private String mServerName; // VPN server name
- private String mDomainSuffices; // space separated list
- private String mRouteList; // space separated list
- private String mSavedUsername;
- private boolean mIsCustomized;
- private transient VpnState mState = VpnState.IDLE;
-
- /** Sets a user-friendly name for this profile. */
- public void setName(String name) {
- mName = name;
- }
-
- public String getName() {
- return mName;
- }
-
- /**
- * Sets an ID for this profile. The caller should make sure the
- * uniqueness of the ID.
- */
- public void setId(String id) {
- mId = id;
- }
-
- public String getId() {
- return mId;
- }
-
- /**
- * Sets the name of the VPN server. Used for DNS lookup.
- */
- public void setServerName(String name) {
- mServerName = name;
- }
-
- public String getServerName() {
- return mServerName;
- }
-
- /**
- * Sets the domain suffices for DNS resolution.
- *
- * @param entries a comma-separated list of domain suffices
- */
- public void setDomainSuffices(String entries) {
- mDomainSuffices = entries;
- }
-
- public String getDomainSuffices() {
- return mDomainSuffices;
- }
-
- /**
- * Sets the routing info for this VPN connection.
- *
- * @param entries a comma-separated list of routes; each entry is in the
- * format of "(network address)/(network mask)"
- */
- public void setRouteList(String entries) {
- mRouteList = entries;
- }
-
- public String getRouteList() {
- return mRouteList;
- }
-
- public void setSavedUsername(String name) {
- mSavedUsername = name;
- }
-
- public String getSavedUsername() {
- return mSavedUsername;
- }
-
- public void setState(VpnState state) {
- mState = state;
- }
-
- public VpnState getState() {
- return ((mState == null) ? VpnState.IDLE : mState);
- }
-
- public boolean isIdle() {
- return (mState == VpnState.IDLE);
- }
-
- /**
- * Returns whether this profile is custom made (as opposed to being
- * created by provided user interface).
- */
- public boolean isCustomized() {
- return mIsCustomized;
- }
-
- /**
- * Returns the VPN type of the profile.
- */
- public abstract VpnType getType();
-
- void setCustomized(boolean customized) {
- mIsCustomized = customized;
- }
-
- protected void readFromParcel(Parcel in) {
- mName = in.readString();
- mId = in.readString();
- mServerName = in.readString();
- mDomainSuffices = in.readString();
- mRouteList = in.readString();
- mSavedUsername = in.readString();
- }
-
- public static final Parcelable.Creator<VpnProfile> CREATOR =
- new Parcelable.Creator<VpnProfile>() {
- public VpnProfile createFromParcel(Parcel in) {
- VpnType type = Enum.valueOf(VpnType.class, in.readString());
- boolean customized = in.readInt() > 0;
- VpnProfile p = new VpnManager(null).createVpnProfile(type,
- customized);
- if (p == null) return null;
- p.readFromParcel(in);
- return p;
- }
-
- public VpnProfile[] newArray(int size) {
- return new VpnProfile[size];
- }
- };
-
- public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeString(getType().toString());
- parcel.writeInt(mIsCustomized ? 1 : 0);
- parcel.writeString(mName);
- parcel.writeString(mId);
- parcel.writeString(mServerName);
- parcel.writeString(mDomainSuffices);
- parcel.writeString(mRouteList);
- parcel.writeString(mSavedUsername);
- }
-
- public int describeContents() {
- return 0;
- }
-}
diff --git a/vpn/java/android/net/vpn/VpnState.java b/vpn/java/android/net/vpn/VpnState.java
deleted file mode 100644
index 6e61f9c..0000000
--- a/vpn/java/android/net/vpn/VpnState.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2009, 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.vpn;
-
-/**
- * Enumeration of all VPN states.
- *
- * A normal VPN connection lifetime starts in {@link IDLE}. When a new
- * connection is about to be set up, it goes to {@link CONNECTING} and then
- * {@link CONNECTED} if successful; back to {@link IDLE} if failed.
- * When the connection is about to be torn down, it goes to
- * {@link DISCONNECTING} and then {@link IDLE}.
- * {@link CANCELLED} is a state when a VPN connection attempt is aborted, and
- * is in transition to {@link IDLE}.
- * The {@link UNUSABLE} state indicates that the profile is not in a state for
- * connecting due to possibly the integrity of the fields or another profile is
- * connecting etc.
- * The {@link UNKNOWN} state indicates that the profile state is to be
- * determined.
- * {@hide}
- */
-public enum VpnState {
- CONNECTING, DISCONNECTING, CANCELLED, CONNECTED, IDLE, UNUSABLE, UNKNOWN
-}
diff --git a/vpn/java/android/net/vpn/VpnType.java b/vpn/java/android/net/vpn/VpnType.java
deleted file mode 100644
index 356f8b1..0000000
--- a/vpn/java/android/net/vpn/VpnType.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2009, 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.vpn;
-
-import com.android.internal.R;
-
-/**
- * Enumeration of all supported VPN types.
- * {@hide}
- */
-public enum VpnType {
- PPTP("PPTP", R.string.pptp_vpn_description, PptpProfile.class),
- L2TP("L2TP", R.string.l2tp_vpn_description, L2tpProfile.class),
- L2TP_IPSEC_PSK("L2TP/IPSec PSK", R.string.l2tp_ipsec_psk_vpn_description,
- L2tpIpsecPskProfile.class),
- L2TP_IPSEC("L2TP/IPSec CRT", R.string.l2tp_ipsec_crt_vpn_description,
- L2tpIpsecProfile.class);
-
- private String mDisplayName;
- private int mDescriptionId;
- private Class<? extends VpnProfile> mClass;
-
- VpnType(String displayName, int descriptionId,
- Class<? extends VpnProfile> klass) {
- mDisplayName = displayName;
- mDescriptionId = descriptionId;
- mClass = klass;
- }
-
- public String getDisplayName() {
- return mDisplayName;
- }
-
- public int getDescriptionId() {
- return mDescriptionId;
- }
-
- public Class<? extends VpnProfile> getProfileClass() {
- return mClass;
- }
-}
diff --git a/vpn/java/com/android/server/vpn/DaemonProxy.java b/vpn/java/com/android/server/vpn/DaemonProxy.java
deleted file mode 100644
index 289ee45..0000000
--- a/vpn/java/com/android/server/vpn/DaemonProxy.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (C) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.vpn;
-
-import android.net.LocalSocket;
-import android.net.LocalSocketAddress;
-import android.net.vpn.VpnManager;
-import android.os.SystemProperties;
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.Serializable;
-
-/**
- * Proxy to start, stop and interact with a VPN daemon.
- * The daemon is expected to accept connection through Unix domain socket.
- * When the proxy successfully starts the daemon, it will establish a socket
- * connection with the daemon, to both send commands to the daemon and receive
- * response and connecting error code from the daemon.
- */
-class DaemonProxy implements Serializable {
- private static final long serialVersionUID = 1L;
- private static final boolean DBG = true;
-
- private static final int WAITING_TIME = 15; // sec
-
- private static final String SVC_STATE_CMD_PREFIX = "init.svc.";
- private static final String SVC_START_CMD = "ctl.start";
- private static final String SVC_STOP_CMD = "ctl.stop";
- private static final String SVC_STATE_RUNNING = "running";
- private static final String SVC_STATE_STOPPED = "stopped";
-
- private static final int END_OF_ARGUMENTS = 255;
-
- private String mName;
- private String mTag;
- private transient LocalSocket mControlSocket;
-
- /**
- * Creates a proxy of the specified daemon.
- * @param daemonName name of the daemon
- */
- DaemonProxy(String daemonName) {
- mName = daemonName;
- mTag = "SProxy_" + daemonName;
- }
-
- String getName() {
- return mName;
- }
-
- void start() throws IOException {
- String svc = mName;
-
- Log.i(mTag, "Start VPN daemon: " + svc);
- SystemProperties.set(SVC_START_CMD, svc);
-
- if (!blockUntil(SVC_STATE_RUNNING, WAITING_TIME)) {
- throw new IOException("cannot start service: " + svc);
- } else {
- mControlSocket = createServiceSocket();
- }
- }
-
- void sendCommand(String ...args) throws IOException {
- OutputStream out = getControlSocketOutput();
- for (String arg : args) outputString(out, arg);
- out.write(END_OF_ARGUMENTS);
- out.flush();
-
- int result = getResultFromSocket(true);
- if (result != args.length) {
- throw new IOException("socket error, result from service: "
- + result);
- }
- }
-
- // returns 0 if nothing is in the receive buffer
- int getResultFromSocket() throws IOException {
- return getResultFromSocket(false);
- }
-
- void closeControlSocket() {
- if (mControlSocket == null) return;
- try {
- mControlSocket.close();
- } catch (IOException e) {
- Log.w(mTag, "close control socket", e);
- } finally {
- mControlSocket = null;
- }
- }
-
- void stop() {
- String svc = mName;
- Log.i(mTag, "Stop VPN daemon: " + svc);
- SystemProperties.set(SVC_STOP_CMD, svc);
- boolean success = blockUntil(SVC_STATE_STOPPED, 5);
- if (DBG) Log.d(mTag, "stopping " + svc + ", success? " + success);
- }
-
- boolean isStopped() {
- String cmd = SVC_STATE_CMD_PREFIX + mName;
- return SVC_STATE_STOPPED.equals(SystemProperties.get(cmd));
- }
-
- private int getResultFromSocket(boolean blocking) throws IOException {
- LocalSocket s = mControlSocket;
- if (s == null) return 0;
- InputStream in = s.getInputStream();
- if (!blocking && in.available() == 0) return 0;
-
- int data = in.read();
- Log.i(mTag, "got data from control socket: " + data);
-
- return data;
- }
-
- private LocalSocket createServiceSocket() throws IOException {
- LocalSocket s = new LocalSocket();
- LocalSocketAddress a = new LocalSocketAddress(mName,
- LocalSocketAddress.Namespace.RESERVED);
-
- // try a few times in case the service has not listen()ed
- IOException excp = null;
- for (int i = 0; i < 10; i++) {
- try {
- s.connect(a);
- return s;
- } catch (IOException e) {
- if (DBG) Log.d(mTag, "service not yet listen()ing; try again");
- excp = e;
- sleep(500);
- }
- }
- throw excp;
- }
-
- private OutputStream getControlSocketOutput() throws IOException {
- if (mControlSocket != null) {
- return mControlSocket.getOutputStream();
- } else {
- throw new IOException("no control socket available");
- }
- }
-
- /**
- * Waits for the process to be in the expected state. The method returns
- * false if after the specified duration (in seconds), the process is still
- * not in the expected state.
- */
- private boolean blockUntil(String expectedState, int waitTime) {
- String cmd = SVC_STATE_CMD_PREFIX + mName;
- int sleepTime = 200; // ms
- int n = waitTime * 1000 / sleepTime;
- for (int i = 0; i < n; i++) {
- if (expectedState.equals(SystemProperties.get(cmd))) {
- if (DBG) {
- Log.d(mTag, mName + " is " + expectedState + " after "
- + (i * sleepTime) + " msec");
- }
- break;
- }
- sleep(sleepTime);
- }
- return expectedState.equals(SystemProperties.get(cmd));
- }
-
- private void outputString(OutputStream out, String s) throws IOException {
- byte[] bytes = s.getBytes();
- out.write(bytes.length);
- out.write(bytes);
- out.flush();
- }
-
- private void sleep(int msec) {
- try {
- Thread.currentThread().sleep(msec);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
-}
diff --git a/vpn/java/com/android/server/vpn/L2tpIpsecPskService.java b/vpn/java/com/android/server/vpn/L2tpIpsecPskService.java
deleted file mode 100644
index 50e0de1..0000000
--- a/vpn/java/com/android/server/vpn/L2tpIpsecPskService.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.vpn;
-
-import android.net.vpn.L2tpIpsecPskProfile;
-
-import java.io.IOException;
-
-/**
- * The service that manages the preshared key based L2TP-over-IPSec VPN
- * connection.
- */
-class L2tpIpsecPskService extends VpnService<L2tpIpsecPskProfile> {
- private static final String IPSEC = "racoon";
-
- @Override
- protected void connect(String serverIp, String username, String password)
- throws IOException {
- L2tpIpsecPskProfile p = getProfile();
- VpnDaemons daemons = getDaemons();
-
- // IPSEC
- daemons.startIpsecForL2tp(serverIp, p.getPresharedKey())
- .closeControlSocket();
-
- sleep(2000); // 2 seconds
-
- // L2TP
- daemons.startL2tp(serverIp,
- (p.isSecretEnabled() ? p.getSecretString() : null),
- username, password);
- }
-}
diff --git a/vpn/java/com/android/server/vpn/L2tpIpsecService.java b/vpn/java/com/android/server/vpn/L2tpIpsecService.java
deleted file mode 100644
index 663b0e8..0000000
--- a/vpn/java/com/android/server/vpn/L2tpIpsecService.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.vpn;
-
-import android.net.vpn.L2tpIpsecProfile;
-import android.security.Credentials;
-
-import java.io.IOException;
-
-/**
- * The service that manages the certificate based L2TP-over-IPSec VPN connection.
- */
-class L2tpIpsecService extends VpnService<L2tpIpsecProfile> {
- private static final String IPSEC = "racoon";
-
- @Override
- protected void connect(String serverIp, String username, String password)
- throws IOException {
- L2tpIpsecProfile p = getProfile();
- VpnDaemons daemons = getDaemons();
-
- // IPSEC
- DaemonProxy ipsec = daemons.startIpsecForL2tp(serverIp,
- Credentials.USER_PRIVATE_KEY + p.getUserCertificate(),
- Credentials.USER_CERTIFICATE + p.getUserCertificate(),
- Credentials.CA_CERTIFICATE + p.getCaCertificate());
- ipsec.closeControlSocket();
-
- sleep(2000); // 2 seconds
-
- // L2TP
- daemons.startL2tp(serverIp,
- (p.isSecretEnabled() ? p.getSecretString() : null),
- username, password);
- }
-}
diff --git a/vpn/java/com/android/server/vpn/L2tpService.java b/vpn/java/com/android/server/vpn/L2tpService.java
deleted file mode 100644
index 784a366..0000000
--- a/vpn/java/com/android/server/vpn/L2tpService.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.vpn;
-
-import android.net.vpn.L2tpProfile;
-
-import java.io.IOException;
-
-/**
- * The service that manages the L2TP VPN connection.
- */
-class L2tpService extends VpnService<L2tpProfile> {
- @Override
- protected void connect(String serverIp, String username, String password)
- throws IOException {
- L2tpProfile p = getProfile();
- getDaemons().startL2tp(serverIp,
- (p.isSecretEnabled() ? p.getSecretString() : null),
- username, password);
- }
-}
diff --git a/vpn/java/com/android/server/vpn/PptpService.java b/vpn/java/com/android/server/vpn/PptpService.java
deleted file mode 100644
index de12710..0000000
--- a/vpn/java/com/android/server/vpn/PptpService.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.vpn;
-
-import android.net.vpn.PptpProfile;
-
-import java.io.IOException;
-
-/**
- * The service that manages the PPTP VPN connection.
- */
-class PptpService extends VpnService<PptpProfile> {
- @Override
- protected void connect(String serverIp, String username, String password)
- throws IOException {
- PptpProfile p = getProfile();
- getDaemons().startPptp(serverIp, username, password,
- p.isEncryptionEnabled());
- }
-}
diff --git a/vpn/java/com/android/server/vpn/VpnConnectingError.java b/vpn/java/com/android/server/vpn/VpnConnectingError.java
deleted file mode 100644
index 3c4ec7d..0000000
--- a/vpn/java/com/android/server/vpn/VpnConnectingError.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.vpn;
-
-import java.io.IOException;
-
-/**
- * Exception thrown when a connecting attempt fails.
- */
-class VpnConnectingError extends IOException {
- private int mErrorCode;
-
- VpnConnectingError(int errorCode) {
- super("Connecting error: " + errorCode);
- mErrorCode = errorCode;
- }
-
- int getErrorCode() {
- return mErrorCode;
- }
-}
diff --git a/vpn/java/com/android/server/vpn/VpnDaemons.java b/vpn/java/com/android/server/vpn/VpnDaemons.java
deleted file mode 100644
index 499195f..0000000
--- a/vpn/java/com/android/server/vpn/VpnDaemons.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.vpn;
-
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * A helper class for managing native VPN daemons.
- */
-class VpnDaemons implements Serializable {
- static final long serialVersionUID = 1L;
- private final String TAG = VpnDaemons.class.getSimpleName();
-
- private static final String MTPD = "mtpd";
- private static final String IPSEC = "racoon";
-
- private static final String L2TP = "l2tp";
- private static final String L2TP_PORT = "1701";
-
- private static final String PPTP = "pptp";
- private static final String PPTP_PORT = "1723";
-
- private static final String VPN_LINKNAME = "vpn";
- private static final String PPP_ARGS_SEPARATOR = "";
-
- private List<DaemonProxy> mDaemonList = new ArrayList<DaemonProxy>();
-
- public DaemonProxy startL2tp(String serverIp, String secret,
- String username, String password) throws IOException {
- return startMtpd(L2TP, serverIp, L2TP_PORT, secret, username, password,
- false);
- }
-
- public DaemonProxy startPptp(String serverIp, String username,
- String password, boolean encryption) throws IOException {
- return startMtpd(PPTP, serverIp, PPTP_PORT, null, username, password,
- encryption);
- }
-
- public DaemonProxy startIpsecForL2tp(String serverIp, String pskKey)
- throws IOException {
- DaemonProxy ipsec = startDaemon(IPSEC);
- ipsec.sendCommand(serverIp, L2TP_PORT, pskKey);
- return ipsec;
- }
-
- public DaemonProxy startIpsecForL2tp(String serverIp, String userKeyKey,
- String userCertKey, String caCertKey) throws IOException {
- DaemonProxy ipsec = startDaemon(IPSEC);
- ipsec.sendCommand(serverIp, L2TP_PORT, userKeyKey, userCertKey,
- caCertKey);
- return ipsec;
- }
-
- public synchronized void stopAll() {
- new DaemonProxy(MTPD).stop();
- new DaemonProxy(IPSEC).stop();
- }
-
- public synchronized void closeSockets() {
- for (DaemonProxy s : mDaemonList) s.closeControlSocket();
- }
-
- public synchronized boolean anyDaemonStopped() {
- for (DaemonProxy s : mDaemonList) {
- if (s.isStopped()) {
- Log.w(TAG, " VPN daemon gone: " + s.getName());
- return true;
- }
- }
- return false;
- }
-
- public synchronized int getSocketError() {
- for (DaemonProxy s : mDaemonList) {
- int errCode = getResultFromSocket(s);
- if (errCode != 0) return errCode;
- }
- return 0;
- }
-
- private synchronized DaemonProxy startDaemon(String daemonName)
- throws IOException {
- DaemonProxy daemon = new DaemonProxy(daemonName);
- mDaemonList.add(daemon);
- daemon.start();
- return daemon;
- }
-
- private int getResultFromSocket(DaemonProxy s) {
- try {
- return s.getResultFromSocket();
- } catch (IOException e) {
- return -1;
- }
- }
-
- private DaemonProxy startMtpd(String protocol,
- String serverIp, String port, String secret, String username,
- String password, boolean encryption) throws IOException {
- ArrayList<String> args = new ArrayList<String>();
- args.addAll(Arrays.asList(protocol, serverIp, port));
- if (secret != null) args.add(secret);
- args.add(PPP_ARGS_SEPARATOR);
- addPppArguments(args, serverIp, username, password, encryption);
-
- DaemonProxy mtpd = startDaemon(MTPD);
- mtpd.sendCommand(args.toArray(new String[args.size()]));
- return mtpd;
- }
-
- private static void addPppArguments(ArrayList<String> args, String serverIp,
- String username, String password, boolean encryption)
- throws IOException {
- args.addAll(Arrays.asList(
- "linkname", VPN_LINKNAME,
- "name", username,
- "password", password,
- "refuse-eap", "nodefaultroute", "usepeerdns",
- "idle", "1800",
- "mtu", "1400",
- "mru", "1400"));
- if (encryption) {
- args.add("+mppe");
- }
- }
-}
diff --git a/vpn/java/com/android/server/vpn/VpnService.java b/vpn/java/com/android/server/vpn/VpnService.java
deleted file mode 100644
index 4966c06..0000000
--- a/vpn/java/com/android/server/vpn/VpnService.java
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
- * Copyright (C) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.vpn;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.net.vpn.VpnManager;
-import android.net.vpn.VpnProfile;
-import android.net.vpn.VpnState;
-import android.os.SystemProperties;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.internal.R;
-
-import java.io.IOException;
-import java.net.DatagramSocket;
-import java.net.InetAddress;
-import java.net.NetworkInterface;
-import java.net.UnknownHostException;
-
-/**
- * The service base class for managing a type of VPN connection.
- */
-abstract class VpnService<E extends VpnProfile> {
- private static final boolean DBG = true;
- private static final int NOTIFICATION_ID = 1;
-
- private static final String DNS1 = "net.dns1";
- private static final String DNS2 = "net.dns2";
- private static final String VPN_DNS1 = "vpn.dns1";
- private static final String VPN_DNS2 = "vpn.dns2";
- private static final String VPN_STATUS = "vpn.status";
- private static final String VPN_IS_UP = "ok";
- private static final String VPN_IS_DOWN = "down";
-
- private static final String REMOTE_IP = "net.ipremote";
- private static final String DNS_DOMAIN_SUFFICES = "net.dns.search";
-
- private final String TAG = VpnService.class.getSimpleName();
-
- E mProfile;
- transient Context mContext;
-
- private VpnState mState = VpnState.IDLE;
- private Throwable mError;
-
- // connection settings
- private String mOriginalDns1;
- private String mOriginalDns2;
- private String mOriginalDomainSuffices;
- private String mLocalIp;
- private String mLocalIf;
-
- private long mStartTime; // VPN connection start time
-
- // for helping managing daemons
- private VpnDaemons mDaemons = new VpnDaemons();
-
- // for helping showing, updating notification
- private transient NotificationHelper mNotification;
-
- /**
- * Establishes a VPN connection with the specified username and password.
- */
- protected abstract void connect(String serverIp, String username,
- String password) throws IOException;
-
- /**
- * Returns the daemons management class for this service object.
- */
- protected VpnDaemons getDaemons() {
- return mDaemons;
- }
-
- /**
- * Returns the VPN profile associated with the connection.
- */
- protected E getProfile() {
- return mProfile;
- }
-
- /**
- * Returns the IP address of the specified host name.
- */
- protected String getIp(String hostName) throws IOException {
- return InetAddress.getByName(hostName).getHostAddress();
- }
-
- void setContext(Context context, E profile) {
- mProfile = profile;
- mContext = context;
- mNotification = new NotificationHelper();
-
- if (VpnState.CONNECTED.equals(mState)) {
- Log.i("VpnService", " recovered: " + mProfile.getName());
- startConnectivityMonitor();
- }
- }
-
- VpnState getState() {
- return mState;
- }
-
- boolean isIdle() {
- return (mState == VpnState.IDLE);
- }
-
- synchronized boolean onConnect(String username, String password) {
- try {
- setState(VpnState.CONNECTING);
-
- mDaemons.stopAll();
- String serverIp = getIp(getProfile().getServerName());
- saveLocalIpAndInterface(serverIp);
- onBeforeConnect();
- connect(serverIp, username, password);
- waitUntilConnectedOrTimedout();
- return true;
- } catch (Throwable e) {
- onError(e);
- return false;
- }
- }
-
- synchronized void onDisconnect() {
- try {
- Log.i(TAG, "disconnecting VPN...");
- setState(VpnState.DISCONNECTING);
- mNotification.showDisconnect();
-
- mDaemons.stopAll();
- } catch (Throwable e) {
- Log.e(TAG, "onDisconnect()", e);
- } finally {
- onFinalCleanUp();
- }
- }
-
- private void onError(Throwable error) {
- // error may occur during or after connection setup
- // and it may be due to one or all services gone
- if (mError != null) {
- Log.w(TAG, " multiple errors occur, record the last one: "
- + error);
- }
- Log.e(TAG, "onError()", error);
- mError = error;
- onDisconnect();
- }
-
- private void onError(int errorCode) {
- onError(new VpnConnectingError(errorCode));
- }
-
-
- private void onBeforeConnect() throws IOException {
- mNotification.disableNotification();
-
- SystemProperties.set(VPN_DNS1, "");
- SystemProperties.set(VPN_DNS2, "");
- SystemProperties.set(VPN_STATUS, VPN_IS_DOWN);
- if (DBG) {
- Log.d(TAG, " VPN UP: " + SystemProperties.get(VPN_STATUS));
- }
- }
-
- private void waitUntilConnectedOrTimedout() throws IOException {
- sleep(2000); // 2 seconds
- for (int i = 0; i < 80; i++) {
- if (mState != VpnState.CONNECTING) {
- break;
- } else if (VPN_IS_UP.equals(
- SystemProperties.get(VPN_STATUS))) {
- onConnected();
- return;
- } else {
- int err = mDaemons.getSocketError();
- if (err != 0) {
- onError(err);
- return;
- }
- }
- sleep(500); // 0.5 second
- }
-
- if (mState == VpnState.CONNECTING) {
- onError(new IOException("Connecting timed out"));
- }
- }
-
- private synchronized void onConnected() throws IOException {
- if (DBG) Log.d(TAG, "onConnected()");
-
- mDaemons.closeSockets();
- saveOriginalDns();
- saveAndSetDomainSuffices();
-
- mStartTime = System.currentTimeMillis();
-
- setState(VpnState.CONNECTED);
- setVpnDns();
-
- startConnectivityMonitor();
- }
-
- private synchronized void onFinalCleanUp() {
- if (DBG) Log.d(TAG, "onFinalCleanUp()");
-
- if (mState == VpnState.IDLE) return;
-
- // keep the notification when error occurs
- if (!anyError()) mNotification.disableNotification();
-
- restoreOriginalDns();
- restoreOriginalDomainSuffices();
- setState(VpnState.IDLE);
-
- SystemProperties.set(VPN_STATUS, VPN_IS_DOWN);
- }
-
- private boolean anyError() {
- return (mError != null);
- }
-
- private void restoreOriginalDns() {
- // restore only if they are not overridden
- String vpnDns1 = SystemProperties.get(VPN_DNS1);
- if (vpnDns1.equals(SystemProperties.get(DNS1))) {
- Log.i(TAG, String.format("restore original dns prop: %s --> %s",
- SystemProperties.get(DNS1), mOriginalDns1));
- Log.i(TAG, String.format("restore original dns prop: %s --> %s",
- SystemProperties.get(DNS2), mOriginalDns2));
- SystemProperties.set(DNS1, mOriginalDns1);
- SystemProperties.set(DNS2, mOriginalDns2);
- }
- }
-
- private void saveOriginalDns() {
- mOriginalDns1 = SystemProperties.get(DNS1);
- mOriginalDns2 = SystemProperties.get(DNS2);
- Log.i(TAG, String.format("save original dns prop: %s, %s",
- mOriginalDns1, mOriginalDns2));
- }
-
- private void setVpnDns() {
- String vpnDns1 = SystemProperties.get(VPN_DNS1);
- String vpnDns2 = SystemProperties.get(VPN_DNS2);
- SystemProperties.set(DNS1, vpnDns1);
- SystemProperties.set(DNS2, vpnDns2);
- Log.i(TAG, String.format("set vpn dns prop: %s, %s",
- vpnDns1, vpnDns2));
- }
-
- private void saveAndSetDomainSuffices() {
- mOriginalDomainSuffices = SystemProperties.get(DNS_DOMAIN_SUFFICES);
- Log.i(TAG, "save original suffices: " + mOriginalDomainSuffices);
- String list = mProfile.getDomainSuffices();
- if (!TextUtils.isEmpty(list)) {
- SystemProperties.set(DNS_DOMAIN_SUFFICES, list);
- }
- }
-
- private void restoreOriginalDomainSuffices() {
- Log.i(TAG, "restore original suffices --> " + mOriginalDomainSuffices);
- SystemProperties.set(DNS_DOMAIN_SUFFICES, mOriginalDomainSuffices);
- }
-
- private void setState(VpnState newState) {
- mState = newState;
- broadcastConnectivity(newState);
- }
-
- private void broadcastConnectivity(VpnState s) {
- VpnManager m = new VpnManager(mContext);
- Throwable err = mError;
- if ((s == VpnState.IDLE) && (err != null)) {
- if (err instanceof UnknownHostException) {
- m.broadcastConnectivity(mProfile.getName(), s,
- VpnManager.VPN_ERROR_UNKNOWN_SERVER);
- } else if (err instanceof VpnConnectingError) {
- m.broadcastConnectivity(mProfile.getName(), s,
- ((VpnConnectingError) err).getErrorCode());
- } else if (VPN_IS_UP.equals(SystemProperties.get(VPN_STATUS))) {
- m.broadcastConnectivity(mProfile.getName(), s,
- VpnManager.VPN_ERROR_CONNECTION_LOST);
- } else {
- m.broadcastConnectivity(mProfile.getName(), s,
- VpnManager.VPN_ERROR_CONNECTION_FAILED);
- }
- } else {
- m.broadcastConnectivity(mProfile.getName(), s);
- }
- }
-
- private void startConnectivityMonitor() {
- new Thread(new Runnable() {
- public void run() {
- Log.i(TAG, "VPN connectivity monitor running");
- try {
- mNotification.update(mStartTime); // to pop up notification
- for (int i = 10; ; i--) {
- long now = System.currentTimeMillis();
-
- boolean heavyCheck = i == 0;
- synchronized (VpnService.this) {
- if (mState != VpnState.CONNECTED) break;
- mNotification.update(now);
-
- if (heavyCheck) {
- i = 10;
- if (checkConnectivity()) checkDns();
- }
- long t = 1000L - System.currentTimeMillis() + now;
- if (t > 100L) VpnService.this.wait(t);
- }
- }
- } catch (InterruptedException e) {
- onError(e);
- }
- Log.i(TAG, "VPN connectivity monitor stopped");
- }
- }).start();
- }
-
- private void saveLocalIpAndInterface(String serverIp) throws IOException {
- DatagramSocket s = new DatagramSocket();
- int port = 80; // arbitrary
- s.connect(InetAddress.getByName(serverIp), port);
- InetAddress localIp = s.getLocalAddress();
- mLocalIp = localIp.getHostAddress();
- NetworkInterface localIf = NetworkInterface.getByInetAddress(localIp);
- mLocalIf = (localIf == null) ? null : localIf.getName();
- if (TextUtils.isEmpty(mLocalIf)) {
- throw new IOException("Local interface is empty!");
- }
- if (DBG) {
- Log.d(TAG, " Local IP: " + mLocalIp + ", if: " + mLocalIf);
- }
- }
-
- // returns false if vpn connectivity is broken
- private boolean checkConnectivity() {
- if (mDaemons.anyDaemonStopped() || isLocalIpChanged()) {
- onError(new IOException("Connectivity lost"));
- return false;
- } else {
- return true;
- }
- }
-
- private void checkDns() {
- String dns1 = SystemProperties.get(DNS1);
- String vpnDns1 = SystemProperties.get(VPN_DNS1);
- if (!dns1.equals(vpnDns1) && dns1.equals(mOriginalDns1)) {
- // dhcp expires?
- setVpnDns();
- }
- }
-
- private boolean isLocalIpChanged() {
- try {
- InetAddress localIp = InetAddress.getByName(mLocalIp);
- NetworkInterface localIf =
- NetworkInterface.getByInetAddress(localIp);
- if (localIf == null || !mLocalIf.equals(localIf.getName())) {
- Log.w(TAG, " local If changed from " + mLocalIf
- + " to " + localIf);
- return true;
- } else {
- return false;
- }
- } catch (IOException e) {
- Log.w(TAG, "isLocalIpChanged()", e);
- return true;
- }
- }
-
- protected void sleep(int ms) {
- try {
- Thread.currentThread().sleep(ms);
- } catch (InterruptedException e) {
- }
- }
-
- // Helper class for showing, updating notification.
- private class NotificationHelper {
- private NotificationManager mNotificationManager = (NotificationManager)
- mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- private Notification mNotification =
- new Notification(R.drawable.vpn_connected, null, 0L);
- private PendingIntent mPendingIntent = PendingIntent.getActivity(
- mContext, 0,
- new VpnManager(mContext).createSettingsActivityIntent(), 0);
- private String mConnectedTitle;
-
- void update(long now) {
- Notification n = mNotification;
- if (now == mStartTime) {
- // to pop up the notification for the first time
- n.when = mStartTime;
- n.tickerText = mConnectedTitle = getNotificationTitle(true);
- } else {
- n.tickerText = null;
- }
- n.setLatestEventInfo(mContext, mConnectedTitle,
- getConnectedNotificationMessage(now),
- mPendingIntent);
- n.flags |= Notification.FLAG_NO_CLEAR;
- n.flags |= Notification.FLAG_ONGOING_EVENT;
- enableNotification(n);
- }
-
- void showDisconnect() {
- String title = getNotificationTitle(false);
- Notification n = new Notification(R.drawable.vpn_disconnected,
- title, System.currentTimeMillis());
- n.setLatestEventInfo(mContext, title,
- getDisconnectedNotificationMessage(),
- mPendingIntent);
- n.flags |= Notification.FLAG_AUTO_CANCEL;
- disableNotification();
- enableNotification(n);
- }
-
- void disableNotification() {
- mNotificationManager.cancel(NOTIFICATION_ID);
- }
-
- private void enableNotification(Notification n) {
- mNotificationManager.notify(NOTIFICATION_ID, n);
- }
-
- private String getNotificationTitle(boolean connected) {
- String formatString = connected
- ? mContext.getString(
- R.string.vpn_notification_title_connected)
- : mContext.getString(
- R.string.vpn_notification_title_disconnected);
- return String.format(formatString, mProfile.getName());
- }
-
- private String getFormattedTime(int duration) {
- int hours = duration / 3600;
- StringBuilder sb = new StringBuilder();
- if (hours > 0) sb.append(hours).append(':');
- sb.append(String.format("%02d:%02d", (duration % 3600 / 60),
- (duration % 60)));
- return sb.toString();
- }
-
- private String getConnectedNotificationMessage(long now) {
- return getFormattedTime((int) (now - mStartTime) / 1000);
- }
-
- private String getDisconnectedNotificationMessage() {
- return mContext.getString(
- R.string.vpn_notification_hint_disconnected);
- }
- }
-}
diff --git a/vpn/java/com/android/server/vpn/VpnServiceBinder.java b/vpn/java/com/android/server/vpn/VpnServiceBinder.java
deleted file mode 100644
index c474ff9..0000000
--- a/vpn/java/com/android/server/vpn/VpnServiceBinder.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.vpn;
-
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.net.vpn.IVpnService;
-import android.net.vpn.L2tpIpsecProfile;
-import android.net.vpn.L2tpIpsecPskProfile;
-import android.net.vpn.L2tpProfile;
-import android.net.vpn.PptpProfile;
-import android.net.vpn.VpnManager;
-import android.net.vpn.VpnProfile;
-import android.net.vpn.VpnState;
-import android.util.Log;
-
-/**
- * The service class for managing a VPN connection. It implements the
- * {@link IVpnService} binder interface.
- */
-public class VpnServiceBinder extends IVpnService.Stub {
- private static final String TAG = VpnServiceBinder.class.getSimpleName();
- private static final boolean DBG = true;
-
- // The actual implementation is delegated to the VpnService class.
- private VpnService<? extends VpnProfile> mService;
-
- private Context mContext;
-
- public VpnServiceBinder(Context context) {
- mContext = context;
- }
-
- @Override
- public synchronized boolean connect(VpnProfile p, final String username,
- final String password) {
- if ((mService != null) && !mService.isIdle()) return false;
- final VpnService s = mService = createService(p);
-
- new Thread(new Runnable() {
- public void run() {
- s.onConnect(username, password);
- }
- }).start();
- return true;
- }
-
- @Override
- public synchronized void disconnect() {
- if (mService == null) return;
- final VpnService s = mService;
- mService = null;
-
- new Thread(new Runnable() {
- public void run() {
- s.onDisconnect();
- }
- }).start();
- }
-
- @Override
- public synchronized String getState(VpnProfile p) {
- if ((mService == null)
- || (!p.getName().equals(mService.mProfile.getName()))) {
- return VpnState.IDLE.toString();
- } else {
- return mService.getState().toString();
- }
- }
-
- @Override
- public synchronized boolean isIdle() {
- return (mService == null || mService.isIdle());
- }
-
- private VpnService<? extends VpnProfile> createService(VpnProfile p) {
- switch (p.getType()) {
- case L2TP:
- L2tpService l2tp = new L2tpService();
- l2tp.setContext(mContext, (L2tpProfile) p);
- return l2tp;
-
- case PPTP:
- PptpService pptp = new PptpService();
- pptp.setContext(mContext, (PptpProfile) p);
- return pptp;
-
- case L2TP_IPSEC_PSK:
- L2tpIpsecPskService psk = new L2tpIpsecPskService();
- psk.setContext(mContext, (L2tpIpsecPskProfile) p);
- return psk;
-
- case L2TP_IPSEC:
- L2tpIpsecService l2tpIpsec = new L2tpIpsecService();
- l2tpIpsec.setContext(mContext, (L2tpIpsecProfile) p);
- return l2tpIpsec;
-
- default:
- return null;
- }
- }
-}
diff --git a/vpn/tests/vpntests/Android.mk b/vpn/tests/vpntests/Android.mk
deleted file mode 100644
index a19fb56..0000000
--- a/vpn/tests/vpntests/Android.mk
+++ /dev/null
@@ -1,14 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_PACKAGE_NAME := FrameworksVpnTests
-
-include $(BUILD_PACKAGE)
-
diff --git a/vpn/tests/vpntests/AndroidManifest.xml b/vpn/tests/vpntests/AndroidManifest.xml
deleted file mode 100644
index d8405f6..0000000
--- a/vpn/tests/vpntests/AndroidManifest.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.frameworks.vpntests">
- <uses-permission android:name="android.permission.RECEIVE_SMS"/>
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.READ_CONTACTS" />
- <uses-permission android:name="android.permission.WRITE_CONTACTS" />
- <uses-permission android:name="android.permission.WAKE_LOCK" />
- <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
- <uses-permission android:name="android.permission.WRITE_APN_SETTINGS" />
- <uses-permission android:name="android.permission.BROADCAST_STICKY" />
-
- <application>
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation
- android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.frameworks.vpntests"
- android:label="Frameworks VPN Tests" />
-</manifest>
diff --git a/vpn/tests/vpntests/src/android/net/vpn/VpnTest.java b/vpn/tests/vpntests/src/android/net/vpn/VpnTest.java
deleted file mode 100755
index 46a57d3..0000000
--- a/vpn/tests/vpntests/src/android/net/vpn/VpnTest.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2009 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.vpn;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.net.vpn.L2tpProfile;
-import android.net.vpn.L2tpIpsecProfile;
-import android.net.vpn.L2tpIpsecPskProfile;
-import android.net.vpn.PptpProfile;
-import android.net.vpn.VpnManager;
-import android.net.vpn.VpnProfile;
-import android.net.vpn.VpnState;
-import android.net.vpn.VpnType;
-import android.os.ConditionVariable;
-import android.os.Parcel;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.text.TextUtils;
-
-/**
- * Unit test class to test VPN api
- * Use the below command to run the vpn unit test only
- * runtest vpntest or
- * adb shell am instrument -e class 'com.android.unit_tests.VpnTest'
- * -w com.android.unit_tests/android.test.InstrumentationTestRunner
- */
-public class VpnTest extends AndroidTestCase {
- private static final String NAME = "a name";
- private static final String SERVER_NAME = "a server name";
- private static final String ID = "some id";
- private static final String SUFFICES = "some suffices";
- private static final String ROUTES = "some routes";
- private static final String SAVED_NAME = "some name";
-
- @Override
- public void setUp() {
- }
-
- @Override
- public void tearDown() {
- }
-
- @SmallTest
- public void testVpnType() {
- testVpnType(VpnType.L2TP);
- testVpnType(VpnType.L2TP_IPSEC);
- testVpnType(VpnType.L2TP_IPSEC_PSK);
- testVpnType(VpnType.PPTP);
- }
-
- @SmallTest
- public void testVpnProfile() {
- VpnState state = VpnState.CONNECTING;
- testVpnProfile(createTestProfile(state), state);
- }
-
- @SmallTest
- public void testGetType() {
- assertEquals(VpnType.L2TP, new L2tpProfile().getType());
- assertEquals(VpnType.L2TP_IPSEC, new L2tpIpsecProfile().getType());
- assertEquals(VpnType.L2TP_IPSEC_PSK,
- new L2tpIpsecPskProfile().getType());
- assertEquals(VpnType.PPTP, new PptpProfile().getType());
- }
-
- @SmallTest
- public void testVpnTypes() {
- assertTrue(VpnManager.getSupportedVpnTypes().length > 0);
- }
-
- @SmallTest
- public void testGetTypeFromManager() {
- VpnManager m = new VpnManager(getContext());
- VpnType[] types = VpnManager.getSupportedVpnTypes();
- for (VpnType t : types) {
- assertEquals(t, m.createVpnProfile(t).getType());
- }
- }
-
- @SmallTest
- public void testParcelable() {
- VpnProfile p = createTestProfile(VpnState.CONNECTED);
- Parcel parcel = Parcel.obtain();
- p.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
-
- // VpnState is transient and not saved in the parcel
- testVpnProfile(VpnProfile.CREATOR.createFromParcel(parcel), null);
- }
-
- @SmallTest
- public void testReceiver() {
- final String profileName = "whatever";
- final VpnState state = VpnState.DISCONNECTING;
- final ConditionVariable cv = new ConditionVariable();
- cv.close();
- BroadcastReceiver r = new BroadcastReceiver() {
- public void onReceive(Context c, Intent i) {
- assertEquals(profileName,
- i.getStringExtra(VpnManager.BROADCAST_PROFILE_NAME));
- assertEquals(state, i.getSerializableExtra(
- VpnManager.BROADCAST_CONNECTION_STATE));
- cv.open();
- }
- };
-
- VpnManager m = new VpnManager(getContext());
- m.registerConnectivityReceiver(r);
- m.broadcastConnectivity(profileName, state);
-
- // fail it if onReceive() doesn't get executed in 5 sec
- assertTrue(cv.block(5000));
- }
-
- private void testVpnType(VpnType type) {
- assertFalse(TextUtils.isEmpty(type.getDisplayName()));
- assertNotNull(type.getProfileClass());
- }
-
- private VpnProfile createTestProfile(VpnState state) {
- VpnProfile p = new L2tpProfile();
- p.setName(NAME);
- p.setServerName(SERVER_NAME);
- p.setId(ID);
- p.setDomainSuffices(SUFFICES);
- p.setRouteList(ROUTES);
- p.setSavedUsername(SAVED_NAME);
- p.setState(state);
- return p;
- }
-
- private void testVpnProfile(VpnProfile p, VpnState state) {
- assertEquals(NAME, p.getName());
- assertEquals(SERVER_NAME, p.getServerName());
- assertEquals(ID, p.getId());
- assertEquals(SUFFICES, p.getDomainSuffices());
- assertEquals(ROUTES, p.getRouteList());
- assertEquals(SAVED_NAME, p.getSavedUsername());
- if (state != null) assertEquals(state, p.getState());
- }
-}
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 2a033d1..8c28319 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -2085,9 +2085,9 @@
transitionTo(mDriverLoadedState);
sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
} else {
- mSupplicantRestartCount = 0;
Log.e(TAG, "Failed " + mSupplicantRestartCount +
" times to start supplicant, unload driver");
+ mSupplicantRestartCount = 0;
transitionTo(mDriverLoadedState);
sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
}