Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8ba63601 authored by Android (Google) Code Review's avatar Android (Google) Code Review Committed by The Android Open Source Project
Browse files

am 15e74b9d: Merge change 1939 into donut

Merge commit '15e74b9d'

* commit '15e74b9d':
  Temporarily suppress flaky test LocationManagerProximityTest until test harness support can be rolled out.
  Fix build breakage
  Revise the ImageButton class description to include information
  Record statistics about whether HTTP connections are reused.
parents 402b24c6 15e74b9d
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
@@ -137,6 +137,8 @@ public final class Checkin {
            CRASHES_TRUNCATED,
            ELAPSED_REALTIME_SEC,
            ELAPSED_UPTIME_SEC,
            HTTP_REQUEST,
            HTTP_REUSED,
            HTTP_STATUS,
            PHONE_GSM_REGISTERED,
            PHONE_GPRS_ATTEMPTED,
@@ -351,6 +353,3 @@ public final class Checkin {
        }
    }
}


+15 −9
Original line number Diff line number Diff line
@@ -349,27 +349,33 @@ public class Contacts {
        }

        /**
         * Adds a person to the My Contacts group.
         * 
         * @param resolver the resolver to use
         * @param personId the person to add to the group
         * @return the URI of the group membership row
         * @throws IllegalStateException if the My Contacts group can't be found
         * @hide Used in vCard parser code.
         */
        public static Uri addToMyContactsGroup(ContentResolver resolver, long personId) {
            long groupId = 0;
        public static long tryGetMyContactsGroupId(ContentResolver resolver) {
            Cursor groupsCursor = resolver.query(Groups.CONTENT_URI, GROUPS_PROJECTION,
                    Groups.SYSTEM_ID + "='" + Groups.GROUP_MY_CONTACTS + "'", null, null);
            if (groupsCursor != null) {
                try {
                    if (groupsCursor.moveToFirst()) {
                        groupId = groupsCursor.getLong(0);
                        return groupsCursor.getLong(0);
                    }
                } finally {
                    groupsCursor.close();
                }
            }
            return 0;
        }

        /**
         * Adds a person to the My Contacts group.
         *
         * @param resolver the resolver to use
         * @param personId the person to add to the group
         * @return the URI of the group membership row
         * @throws IllegalStateException if the My Contacts group can't be found
         */
        public static Uri addToMyContactsGroup(ContentResolver resolver, long personId) {
            long groupId = tryGetMyContactsGroupId(resolver);
            if (groupId == 0) {
                throw new IllegalStateException("Failed to find the My Contacts group");
            }
+29 −3
Original line number Diff line number Diff line
@@ -27,9 +27,35 @@ import java.util.Map;

/**
 * <p>
 * An image button displays an image that can be pressed, or clicked, by the
 * user.
 * </p>
 * Displays a button with an image (instead of text) that can be pressed 
 * or clicked by the user. By default, an ImageButton looks like a regular 
 * {@link android.widget.Button}, with the standard button background
 * that changes color during different button states. The image on the surface
 * of the button is defined either by the {@code android:src} attribute in the
 * {@code &lt;ImageButton&gt;} XML element or by the 
 * {@link #setImageResource(int)} method.</p>
 * 
 * <p>To remove the standard button background image, define your own 
 * background image or set the background color to be transparent.</p>
 * <p>To indicate the different button states (focused, selected, etc.), you can
 * define a different image for each state. E.g., a blue image by default, an
 * orange one for when focused, and a yellow one for when pressed. An easy way to
 * do this is with an XML drawable "selector." For example:</p>
 * <pre>
 * &lt;?xml version="1.0" encoding="utf-8"?&gt;
 * &lt;selector xmlns:android="http://schemas.android.com/apk/res/android"&gt;
 *     &lt;item android:drawable="@drawable/button_normal" /&gt; &lt;!-- default --&gt;
 *     &lt;item android:state_pressed="true"
 *           android:drawable="@drawable/button_pressed" /&gt; &lt;!-- pressed --&gt;
 *     &lt;item android:state_focused="true"
 *           android:drawable="@drawable/button_focused" /&gt; &lt;!-- focused --&gt;
 * &lt;/selector&gt;</pre>
 *
 * <p>Save the XML file in your project {@code res/drawable/} folder and then 
 * reference it as a drawable for the source of your ImageButton (in the 
 * {@code android:src} attribute). Android will automatically change the image 
 * based on the state of the button and the corresponding images
 * defined in the XML.</p>
 *
 * <p><strong>XML attributes</strong></p>
 * <p>
+86 −30
Original line number Diff line number Diff line
@@ -37,6 +37,10 @@ import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.LayeredSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.scheme.SocketFactory;
import org.apache.http.impl.client.EntityEnclosingRequestWrapper;
import org.apache.http.impl.client.RequestWrapper;
import org.apache.http.params.HttpParams;
@@ -44,6 +48,8 @@ import org.apache.http.protocol.HttpContext;
import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache;

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;

@@ -66,25 +72,22 @@ public class GoogleHttpClient implements HttpClient {

    private final AndroidHttpClient mClient;
    private final ContentResolver mResolver;
    private final String mUserAgent;
    private final String mAppName, mUserAgent;
    private final ThreadLocal mConnectionAllocated = new ThreadLocal<Boolean>();

    /**
     * Create an HTTP client.  Normally one client is shared throughout an app.
     * @param resolver to use for accessing URL rewriting rules.
     * @param userAgent to report in your HTTP requests.
     * @deprecated Use {@link #GoogleHttpClient(android.content.ContentResolver, String, boolean)}
     * Create an HTTP client without SSL session persistence.
     * @deprecated Use {@link #GoogleHttpClient(android.content.Context, String, boolean)}
     */
    public GoogleHttpClient(ContentResolver resolver, String userAgent) {
        mClient = AndroidHttpClient.newInstance(userAgent);
        mResolver = resolver;
        mUserAgent = userAgent;
        mUserAgent = mAppName = userAgent;
    }

    /**
     * GoogleHttpClient(Context, String, boolean) - without SSL session
     * persistence.
     *
     * @deprecated use Context instead of ContentResolver.
     * Create an HTTP client without SSL session persistence.
     * @deprecated Use {@link #GoogleHttpClient(android.content.Context, String, boolean)}
     */
    public GoogleHttpClient(ContentResolver resolver, String appAndVersion,
            boolean gzipCapable) {
@@ -111,21 +114,70 @@ public class GoogleHttpClient implements HttpClient {
     * headers.  Needed because Google servers require gzip in the User-Agent
     * in order to return gzip'd content.
     */
    public GoogleHttpClient(Context context, String appAndVersion,
        boolean gzipCapable) {
        this(context.getContentResolver(), SSLClientSessionCacheFactory.getCache(context),
    public GoogleHttpClient(Context context, String appAndVersion, boolean gzipCapable) {
        this(context.getContentResolver(),
                SSLClientSessionCacheFactory.getCache(context),
                appAndVersion, gzipCapable);
    }

    private GoogleHttpClient(ContentResolver resolver, SSLClientSessionCache cache,
    private GoogleHttpClient(ContentResolver resolver,
            SSLClientSessionCache cache,
            String appAndVersion, boolean gzipCapable) {
        String userAgent = appAndVersion + " (" + Build.DEVICE + " " + Build.ID + ")";
        if (gzipCapable) {
            userAgent = userAgent + "; gzip";
        }

        mClient = AndroidHttpClient.newInstance(userAgent, cache);
        mResolver = resolver;
        mAppName = appAndVersion;
        mUserAgent = userAgent;

        // Wrap all the socket factories with the appropriate wrapper.  (Apache
        // HTTP, curse its black and stupid heart, inspects the SocketFactory to
        // see if it's a LayeredSocketFactory, so we need two wrapper classes.)
        SchemeRegistry registry = getConnectionManager().getSchemeRegistry();
        for (String name : registry.getSchemeNames()) {
            Scheme scheme = registry.unregister(name);
            SocketFactory sf = scheme.getSocketFactory();
            if (sf instanceof LayeredSocketFactory) {
                sf = new WrappedLayeredSocketFactory((LayeredSocketFactory) sf);
            } else {
                sf = new WrappedSocketFactory(sf);
            }
            registry.register(new Scheme(name, sf, scheme.getDefaultPort()));
        }
    }

    /**
     * Delegating wrapper for SocketFactory records when sockets are connected.
     * We use this to know whether a connection was created vs reused, to
     * gather per-app statistics about connection reuse rates.
     */
    private class WrappedSocketFactory implements SocketFactory {
        private SocketFactory mDelegate;
        private WrappedSocketFactory(SocketFactory delegate) { mDelegate = delegate; }
        public final Socket createSocket() throws IOException { return mDelegate.createSocket(); }
        public final boolean isSecure(Socket s) { return mDelegate.isSecure(s); }

        public final Socket connectSocket(
                Socket s, String h, int p,
                InetAddress la, int lp, HttpParams params) throws IOException {
            mConnectionAllocated.set(Boolean.TRUE);
            return mDelegate.connectSocket(s, h, p, la, lp, params);
        }
    }

    /** Like WrappedSocketFactory, but for the LayeredSocketFactory subclass. */
    private class WrappedLayeredSocketFactory
            extends WrappedSocketFactory implements LayeredSocketFactory {
        private LayeredSocketFactory mDelegate;
        private WrappedLayeredSocketFactory(LayeredSocketFactory sf) { super(sf); mDelegate = sf; }

        public final Socket createSocket(Socket s, String host, int port, boolean autoClose)
                throws IOException {
            return mDelegate.createSocket(s, host, port, autoClose);
        }
    }

    /**
@@ -140,24 +192,21 @@ public class GoogleHttpClient implements HttpClient {
    public HttpResponse executeWithoutRewriting(
            HttpUriRequest request, HttpContext context)
            throws IOException {
        String code = "Error";
        int code = -1;
        long start = SystemClock.elapsedRealtime();
        try {
            HttpResponse response;
            mConnectionAllocated.set(null);

            if (NetworkStatsEntity.shouldLogNetworkStats()) {
                // TODO: if we're logging network stats, and if the apache library is configured
                // to follow redirects, count each redirect as an additional round trip.

            // see if we're logging network stats.
            boolean logNetworkStats = NetworkStatsEntity.shouldLogNetworkStats();

            if (logNetworkStats) {
                int uid = android.os.Process.myUid();
                long startTx = NetStat.getUidTxBytes(uid);
                long startRx = NetStat.getUidRxBytes(uid);

                response = mClient.execute(request, context);
                code = Integer.toString(response.getStatusLine().getStatusCode());

                HttpEntity origEntity = response == null ? null : response.getEntity();
                if (origEntity != null) {
                    // yeah, we compute the same thing below.  we do need to compute this here
@@ -165,30 +214,37 @@ public class GoogleHttpClient implements HttpClient {
                    long now = SystemClock.elapsedRealtime();
                    long elapsed = now - start;
                    NetworkStatsEntity entity = new NetworkStatsEntity(origEntity,
                            mUserAgent, uid, startTx, startRx,
                            mAppName, uid, startTx, startRx,
                            elapsed /* response latency */, now /* processing start time */);
                    response.setEntity(entity);
                }
            } else {
                response = mClient.execute(request, context);
                code = Integer.toString(response.getStatusLine().getStatusCode());
            }

            code = response.getStatusLine().getStatusCode();
            return response;
        } catch (IOException e) {
            code = "IOException";
            throw e;
        } finally {
            // Record some statistics to the checkin service about the outcome.
            // Note that this is only describing execute(), not body download.
            try {
                long elapsed = SystemClock.elapsedRealtime() - start;
                ContentValues values = new ContentValues();
                values.put(Checkin.Stats.TAG,
                         Checkin.Stats.Tag.HTTP_STATUS + ":" +
                         mUserAgent + ":" + code);
                values.put(Checkin.Stats.COUNT, 1);
                values.put(Checkin.Stats.SUM, elapsed / 1000.0);

                values.put(Checkin.Stats.TAG, Checkin.Stats.Tag.HTTP_REQUEST + ":" + mAppName);
                mResolver.insert(Checkin.Stats.CONTENT_URI, values);

                // No sockets and no exceptions means we successfully reused a connection
                if (mConnectionAllocated.get() == null && code >= 0) {
                    values.put(Checkin.Stats.TAG, Checkin.Stats.Tag.HTTP_REUSED + ":" + mAppName);
                    mResolver.insert(Checkin.Stats.CONTENT_URI, values);
                }

                String status = code < 0 ? "IOException" : Integer.toString(code);
                values.put(Checkin.Stats.TAG,
                         Checkin.Stats.Tag.HTTP_STATUS + ":" + mAppName + ":" + status);
                mResolver.insert(Checkin.Stats.CONTENT_URI, values);
            } catch (Exception e) {
                Log.e(TAG, "Error recording stats", e);
+4 −1
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.location.LocationManager;
import android.provider.Settings;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.Suppress;
import android.util.Log;

/**
@@ -37,9 +38,11 @@ import android.util.Log;
 *  adb shell am instrument -e class android.location.LocationManagerProximityTest \
 *     -w android.core/android.test.InstrumentationTestRunner
 *     
 * This test requires that the "Allow mock locations" setting be enabled     
 * This test requires that the "Allow mock locations" setting be enabled.
 * To ensure reliable results, all location providers should be disabled.
 * 
 */
@Suppress
@MediumTest
public class LocationManagerProximityTest extends AndroidTestCase {