├── .gitignore ├── Android.mk ├── README.md ├── pom.xml ├── protos-repo └── CheckIn.proto └── src └── com └── google ├── android ├── AndroidAuth.java ├── AndroidContext.java └── AndroidRequestKeys.java ├── auth ├── AuthClient.java ├── AuthCrypto.java ├── AuthRequest.java ├── AuthResponse.java ├── AuthType.java ├── DataField.java ├── DataMap.java ├── DataMapReader.java └── DataMapWriter.java ├── c2dm └── C2DMClient.java ├── checkin ├── CheckinClient.java ├── CheckinResponse.java ├── Request.java └── Response.java └── tools ├── Base64.java ├── Client.java ├── RequestContext.java └── SignatureTools.java /.gitignore: -------------------------------------------------------------------------------- 1 | ../.gitignore -------------------------------------------------------------------------------- /Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | include $(CLEAR_VARS) 3 | 4 | LOCAL_MODULE := JGoogle 5 | LOCAL_STATIC_JAVA_LIBRARIES := wire-runtime 6 | LOCAL_SRC_FILES := $(call all-java-files-under, src) 7 | 8 | include $(BUILD_STATIC_JAVA_LIBRARY) 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | JGoogle 2 | ===== 3 | 4 | An inofficial Google Client library, with focus on Android. Part of μg Project. 5 | 6 | Building 7 | -------- 8 | ### Using AOSP build system 9 | 1. Add JGoogle to the build system (e.g. inside external/JGoogle) 10 | 2. Build using `make JGoogle` 11 | 12 | ### Using Maven 13 | 1. Build using `mvn compile jar:jar` 14 | 15 | License 16 | ------- 17 | > Copyright 2012-2013 μg Project Team 18 | 19 | > Licensed under the Apache License, Version 2.0 (the "License"); 20 | > you may not use this file except in compliance with the License. 21 | > You may obtain a copy of the License at 22 | 23 | > http://www.apache.org/licenses/LICENSE-2.0 24 | 25 | > Unless required by applicable law or agreed to in writing, software 26 | > distributed under the License is distributed on an "AS IS" BASIS, 27 | > WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 28 | > See the License for the specific language governing permissions and 29 | > limitations under the License. 30 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | org.microg.jgoogle 5 | JGoogle 6 | 0.1 7 | jar 8 | JGoogle 9 | 10 | 11 | 12 | com.google.android 13 | android 14 | 4.1.1.4 15 | provided 16 | 17 | 18 | com.squareup.wire 19 | wire-runtime 20 | 1.5.1 21 | 22 | 23 | 24 | ${project.artifactId} 25 | src 26 | 27 | 28 | org.apache.maven.plugins 29 | maven-jar-plugin 30 | 2.3.1 31 | 32 | 33 | false 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /protos-repo/CheckIn.proto: -------------------------------------------------------------------------------- 1 | option java_package = "com.google.android.checkin"; 2 | 3 | option java_outer_classname = "CheckInProto"; 4 | 5 | message Request { 6 | message DeviceConfig { 7 | optional int32 touchScreen = 1; // ConfigurationInfo.reqTouchScreen 8 | optional int32 keyboardType = 2; // ConfigurationInfo.reqKeyboardType 9 | optional int32 navigation = 3; // ConfigurationInfo.reqNavigation 10 | optional int32 screenLayout = 4; // ConfigurationInfo.screenLayout 11 | optional bool hasHardKeyboard = 5; // ConfigurationInfo.reqInputFeatures & ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD 12 | optional bool hasFiveWayNavigation = 6; // ConfigurationInfo.reqInputFeatures & ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV 13 | optional int32 densityDpi = 7; // DisplayMetrics.densityDpi 14 | optional int32 glEsVersion = 8; // ConfigurationInfo.reqGlEsVersion 15 | repeated string sharedLibrary = 9; // PackageManager.getSystemSharedLibraryNames 16 | repeated string availableFeature = 10; // PackageManager.getSystemAvailableFeatures 17 | repeated string nativePlatform = 11; // Build.CPU_ABI (and Build.CPU_ABI2 != "unknown") 18 | optional int32 widthPixels = 12; // DisplayMetrics.widthPixels 19 | optional int32 heightPixels = 13; // DisplayMetrics.heightPixels 20 | repeated string locale = 14; // Context.getAssets.getLocales 21 | repeated string glExtension = 15; // GLES10.glGetString(GLES10.GL_EXTENSIONS) 22 | optional int32 deviceClass = 16; // unused 23 | optional int32 maxApkDownloadSizeMb = 17; // unused 24 | } 25 | message Checkin { 26 | message Event { 27 | optional string tag = 1; 28 | optional string value = 2; 29 | optional int64 timeMs = 3; 30 | } 31 | message Statistic { 32 | optional string tag = 1; 33 | optional int32 count = 2; 34 | optional float sum = 3; 35 | } 36 | message Build { 37 | optional string fingerprint = 1; // Build.FINGERPRINT 38 | optional string hardware = 2; // Build.HARDWARE 39 | optional string brand = 3; // Build.BRAND 40 | optional string radio = 4; // Build.getRadioVersion() 41 | optional string bootloader = 5; // Build.BOOTLOADER 42 | optional string clientId = 6; // GoogleSettingsContract.Partner["client_id"] 43 | optional int64 time = 7; // Build.TIME / 1000L 44 | optional int32 packageVersionCode = 8; // PackageInfo.versionCode 45 | optional string device = 9; // Build.DEVICE 46 | optional int32 sdkVersion = 10; // Build.VERSION.SDK_INT 47 | optional string model = 11; // Build.MODEL 48 | optional string manufacturer = 12; // Build.MANUFACTURER 49 | optional string product = 13; // Build.PRODUCT 50 | optional bool otaInstalled = 14; // fileExists("/system/recovery-from-boot.p") 51 | } 52 | 53 | optional Build build = 1; 54 | optional int64 lastCheckinMs = 2; // or 0 55 | repeated Event event = 3; 56 | repeated Statistic stat = 4; 57 | repeated string requestedGroup = 5; // unused 58 | optional string cellOperator = 6; // TelephonyManager.getNetworkOperator != null|empty 59 | optional string simOperator = 7; // TelephonyManager.getSimOperator != null|empty 60 | optional string roaming = 8; // "mobile/notmobile/unknown-roaming/notroaming/unknown" 61 | optional int32 userNumber = 9; // UserHandle.myUserId 62 | } 63 | optional string imei = 1; // unused 64 | optional int64 androidId = 2; // Gservices["android_id"] or 0 65 | optional string digest = 3; // Gservices["digest"] or "" 66 | optional Checkin checkin = 4; 67 | optional string desiredBuild = 5; // unused 68 | optional string locale = 6; // Locale.toString 69 | optional int64 loggingId = 7; // GoogleSettingsContract.Partner["logging_id2"] 70 | optional string marketCheckin = 8; // unused 71 | repeated string macAddress = 9; // NetworkInfo.getExtraInfo, WifiInfo.getMacAddress (12 hex-digits) 72 | optional string meid = 10; // TelephonyManager.getDeviceId (14 hex-digits) 73 | repeated string accountCookie = 11; // "[]" followed by "" 74 | optional string timeZone = 12; // TimeZone.getId 75 | optional fixed64 securityToken = 13; 76 | optional int32 version = 14; // 3 if securityToken != 0 OR androidId == 0 77 | repeated string otaCert = 15; // SHA-1 of each in /system/etc/security/otacerts.zip or "--IOException--" or "--no-output--" 78 | optional string serial = 16; // Build.SERIAL != "unknown" 79 | optional string esn = 17; // TelephonyManager.getDeviceId (8 hex-digits) 80 | optional DeviceConfig deviceConfiguration = 18; 81 | repeated string macAddressType = 19; // "ethernet", "wifi" 82 | optional int32 fragment = 20; // unknown (use 0) 83 | optional string userName = 21; // unused 84 | optional int32 userSerialNumber = 22; // UserManager.getUserSerialNumber (if != 0) 85 | } 86 | 87 | message Response { 88 | message Intent { 89 | message Extra { 90 | optional string name = 6; 91 | optional string value = 7; 92 | } 93 | optional string action = 1; 94 | optional string dataUri = 2; 95 | optional string mimeType = 3; 96 | optional string javaClass = 4; 97 | repeated Extra extra = 5; 98 | } 99 | 100 | message GservicesSetting { 101 | optional bytes name = 1; 102 | optional bytes value = 2; 103 | } 104 | 105 | optional bool statsOk = 1; 106 | repeated Intent intent = 2; 107 | optional int64 timeMs = 3; 108 | optional string digest = 4; 109 | repeated GservicesSetting setting = 5; 110 | optional bool marketOk = 6; 111 | optional fixed64 androidId = 7; 112 | optional fixed64 securityToken = 8; 113 | optional bool settingsDiff = 9; 114 | repeated string deleteSetting = 10; 115 | } 116 | -------------------------------------------------------------------------------- /src/com/google/android/AndroidAuth.java: -------------------------------------------------------------------------------- 1 | package com.google.android; 2 | 3 | import com.google.auth.*; 4 | import com.google.tools.Client; 5 | 6 | public class AndroidAuth extends AuthClient implements AndroidRequestKeys.UserMetrics { 7 | 8 | private final static String ACCOUNT_TYPE_HOSTED_OR_GOOGLE = "HOSTED_OR_GOOGLE"; 9 | private static final String REQUEST_URL_ANDROID_CLIENT_AUTH = "https://android.clients.google.com/auth"; 10 | private final static String SERVICE_DEFAULT_AC2DM = "ac2dm"; 11 | private final static String SOURCE_ANDROID = "android"; 12 | 13 | private AuthRequest createBaseRequest(final AndroidContext info) { 14 | final AuthRequest request = new AuthRequest(); 15 | request.setRequestUrl(REQUEST_URL_ANDROID_CLIENT_AUTH); 16 | request.putData(DataField.ACCOUNT_TYPE, ACCOUNT_TYPE_HOSTED_OR_GOOGLE); 17 | request.putData(DataField.SOURCE, SOURCE_ANDROID); 18 | request.putData(DataField.SERVICE, SERVICE_DEFAULT_AC2DM); 19 | request.putData(DataField.EMAIL, info.getEmail()); 20 | if (info.getAndroidId() != null && info.getAndroidId() != Long.MIN_VALUE) { 21 | request.putData(DataField.ANDROID_ID, info.getAndroidIdHex()); 22 | } 23 | String[] locale = info.get(KEY_LOCALE, "en_US").split("_"); 24 | if (locale.length != 2) { 25 | locale = new String[]{"en", "US"}; 26 | } 27 | request.putData(DataField.DEVICE_COUNTRY, locale[1]); 28 | request.putData(DataField.OPERATOR_COUNTRY, locale[1]); 29 | request.putData(DataField.LANGUAGE, locale[0]); 30 | request.putData(DataField.SDK_VERSION, Integer.toString(info.getSdkVersion())); 31 | request.setUserAgent("GoogleLoginService/1.3 (" + info.getBuildDevice() + " " + info.getBuildId() + ")"); 32 | return request; 33 | } 34 | 35 | public String getAuthToken(final AndroidContext info, final String masterToken, final String service) { 36 | return getAuthToken(info, masterToken, service, AuthType.MasterToken); 37 | } 38 | 39 | public String getAuthToken(final AndroidContext info, final String auth, final String service, 40 | final AuthType authType) { 41 | final AuthRequest request = createBaseRequest(info); 42 | saveAuthInRequest(request, authType, auth); 43 | request.putData(DataField.SERVICE, service); 44 | final AuthResponse response = sendRequest(request); 45 | if (DEBUG) { 46 | System.out.println(response.toString()); 47 | } 48 | return response.getData(DataField.AUTH_TOKEN); 49 | } 50 | 51 | public String getAuthToken(final AndroidContext info, final String masterToken, final String service, 52 | final String packageName, final String packageSignature, 53 | final boolean storedPermission) { 54 | final DataMapReader response = 55 | getAuthTokenResponse(info, masterToken, service, packageName, packageSignature, storedPermission); 56 | return response.getData(DataField.AUTH_TOKEN); 57 | } 58 | 59 | public DataMapReader getAuthTokenResponse(final AndroidContext info, final String auth, final String service, 60 | final String packageName, final String packageSignature, 61 | final boolean storedPermission) { 62 | return getAuthTokenResponse(info, auth, service, packageName, packageSignature, storedPermission, 63 | AuthType.MasterToken); 64 | } 65 | 66 | public DataMapReader getAuthTokenResponse(final AndroidContext info, final String auth, final String service, 67 | final String packageName, final String packageSignature, 68 | final boolean storedPermission, final AuthType authType) { 69 | if (DEBUG) { 70 | System.out.println( 71 | "getAuthToken: " + service + " | " + hide(auth) + " | " + packageName + " | " + packageSignature + 72 | " | " + authType); 73 | } 74 | final AuthRequest request = createBaseRequest(info); 75 | saveAuthInRequest(request, authType, auth); 76 | request.putData(DataField.SERVICE, service); 77 | request.putData(DataField.PACKAGE_NAME, packageName); 78 | request.putData(DataField.PACKAGE_SIGNATURE, packageSignature); 79 | request.putData(DataField.STORED_PERMISSION, !storedPermission ? "1" : "0"); 80 | final AuthResponse response = sendRequest(request); 81 | if (DEBUG) { 82 | System.out.println(response.toString()); 83 | } 84 | return response; 85 | } 86 | 87 | public String getMasterToken(final AndroidContext info, final String auth, final AuthType authType) { 88 | final DataMapReader response = getMasterTokenResponse(info, auth, authType); 89 | return response.getData(DataField.MASTER_TOKEN); 90 | } 91 | 92 | public String getMasterToken(final AndroidContext info, final String password, final AuthType authType, 93 | final String service) { 94 | final DataMapReader response = getMasterTokenResponse(info, password, authType, service); 95 | return response.getData(DataField.MASTER_TOKEN); 96 | } 97 | 98 | public DataMapReader getMasterTokenResponse(final AndroidContext info, final String auth, final AuthType authType) { 99 | return getMasterTokenResponse(info, auth, authType, null); 100 | } 101 | 102 | public DataMapReader getMasterTokenResponse(final AndroidContext info, final String auth, final AuthType authType, 103 | final String service) { 104 | final AuthRequest request = createBaseRequest(info); 105 | saveAuthInRequest(request, authType, auth); 106 | if (service != null) { 107 | request.putData(DataField.SERVICE, service); 108 | } 109 | final AuthResponse response = sendRequest(request); 110 | if (Client.DEBUG) { 111 | System.out.println(response.toString()); 112 | } 113 | return response; 114 | } 115 | 116 | private static String hide(final String s) { 117 | final StringBuilder sb = new StringBuilder(); 118 | for (int i = 0; i < s.length(); i++) { 119 | if (i % 2 == 1) { 120 | sb.append("*"); 121 | } else { 122 | sb.append(s.charAt(i)); 123 | } 124 | } 125 | return sb.toString(); 126 | } 127 | 128 | private static void saveAuthInRequest(final AuthRequest request, final AuthType type, final String auth) { 129 | switch (type) { 130 | case MasterToken: 131 | request.putData(DataField.MASTER_TOKEN, auth); 132 | break; 133 | case Password: 134 | request.putData(DataField.PASSWORD, auth); 135 | break; 136 | case EncryptedPassword: 137 | request.putData(DataField.ENCRYPTED_PASSWORD, auth); 138 | break; 139 | case AuthToken: 140 | request.putData(DataField.AUTH_TOKEN, auth); 141 | break; 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/com/google/android/AndroidContext.java: -------------------------------------------------------------------------------- 1 | package com.google.android; 2 | 3 | import com.google.tools.RequestContext; 4 | 5 | import java.util.*; 6 | 7 | public class AndroidContext extends RequestContext implements 8 | AndroidRequestKeys.BuildMetrics, AndroidRequestKeys.DeviceMetrics, 9 | AndroidRequestKeys.OperatorMetrics, AndroidRequestKeys.DeviceIdentifiers, 10 | AndroidRequestKeys.UserIdentifiers, AndroidRequestKeys.UserMetrics { 11 | 12 | static Random rand = new Random(); 13 | 14 | /** 15 | * The base device is a Galaxy Nexus with random data. 16 | * @return corresponding AndroidContext 17 | */ 18 | public static AndroidContext baseDevice() { 19 | return new AndroidContext().set(KEY_DEVICE_NATIVE_PLATFORM, Arrays.asList("armeabi-v7a","armeabi")) 20 | .setBuildBootloader("PRIMELA03") 21 | .buildFingerprint("google/yakju/maguro:4.1.1/JRO03C/398337:user/release-keys") 22 | .buildHardware("tuna").buildId("JRO03C").buildBrand("Google") 23 | .setBuildRadio("I9250XXLA2").buildClientId("android-google") 24 | .buildTime(new Date().getTime()).sdkVersion(16) 25 | .buildDevice("maguro").buildModel("Galaxy Nexus") 26 | .buildManufacturer("Samsung").buildProduct("yakju").otaInstalled(false) 27 | .operator("310260", "T-Mobile USA").timeZone("America/New_York") 28 | .roaming("mobile-notroaming").userNumber(0).loggingId(rand.nextLong()) 29 | .otaCert("71Q6Rn2DDZl1zPDVaaeEHItd").locale(Locale.US) 30 | .serial(randomSerialNumber()).setAndroidId(0).setDeviceDensityDpi(320) 31 | .setDigest("1-929a0dca0eee55513280171a8585da7dcd3700f8") 32 | .macAddress(randomMacAddress()).meid(randomMeid()).deviceTouchScreen(3) 33 | .deviceKeyboardType(1).deviceNavigation(1).deviceScreenLayout(2) 34 | .deviceGlEsVersion(131072).deviceWidthPixels(720) 35 | .deviceHeightPixels(1184).deviceSharedLibraries( 36 | Arrays.asList("android.test.runner", "com.android.future.usb.accessory", 37 | "com.android.location.provider", "com.android.nfc_extras", 38 | "com.google.android.maps", "com.google.android.media.effects", 39 | "com.google.widevine.software.drm", "javax.obex")).deviceFeatures( 40 | Arrays.asList("android.hardware.bluetooth", "android.hardware.camera", 41 | "android.hardware.camera.autofocus", "android.hardware.camera.flash", 42 | "android.hardware.camera.front", "android.hardware.faketouch", 43 | "android.hardware.location", "android.hardware.location.gps", 44 | "android.hardware.location.network", "android.hardware.microphone", 45 | "android.hardware.nfc", "android.hardware.screen.landscape", 46 | "android.hardware.screen.portrait", "android.hardware.sensor.accelerometer", 47 | "android.hardware.sensor.barometer", "android.hardware.sensor.compass", 48 | "android.hardware.sensor.gyroscope", "android.hardware.sensor.light", 49 | "android.hardware.sensor.proximity", "android.hardware.telephony", 50 | "android.hardware.telephony.gsm", "android.hardware.touchscreen", 51 | "android.hardware.touchscreen.multitouch", 52 | "android.hardware.touchscreen.multitouch.distinct", 53 | "android.hardware.touchscreen.multitouch.jazzhand", 54 | "android.hardware.usb.accessory", "android.hardware.usb.host", 55 | "android.hardware.wifi", "android.hardware.wifi.direct", 56 | "android.software.live_wallpaper", "android.software.sip", 57 | "android.software.sip.voip", "com.cyanogenmod.android", 58 | "com.cyanogenmod.nfc.enhanced", "com.google.android.feature.GOOGLE_BUILD", 59 | "com.nxp.mifare", "com.tmobile.software.themes")).deviceLocales( 60 | Arrays.asList("af", "af_ZA", "am", "am_ET", "ar", "ar_EG", "bg", "bg_BG", "ca", "ca_ES", "cs", 61 | "cs_CZ", "da", "da_DK", "de", "de_AT", "de_CH", "de_DE", "de_LI", "el", "el_GR", 62 | "en", "en_AU", "en_CA", "en_GB", "en_NZ", "en_SG", "en_US", "es", "es_ES", 63 | "es_US", "fa", "fa_IR", "fi", "fi_FI", "fr", "fr_BE", "fr_CA", "fr_CH", "fr_FR", 64 | "hi", "hi_IN", "hr", "hr_HR", "hu", "hu_HU", "in", "in_ID", "it", "it_CH", 65 | "it_IT", "iw", "iw_IL", "ja", "ja_JP", "ko", "ko_KR", "lt", "lt_LT", "lv", 66 | "lv_LV", "ms", "ms_MY", "nb", "nb_NO", "nl", "nl_BE", "nl_NL", "pl", "pl_PL", 67 | "pt", "pt_BR", "pt_PT", "rm", "rm_CH", "ro", "ro_RO", "ru", "ru_RU", "sk", 68 | "sk_SK", "sl", "sl_SI", "sr", "sr_RS", "sv", "sv_SE", "sw", "sw_TZ", "th", 69 | "th_TH", "tl", "tl_PH", "tr", "tr_TR", "ug", "ug_CN", "uk", "uk_UA", "vi", 70 | "vi_VN", "zh_CN", "zh_TW", "zu", "zu_ZA")).glExtensions( 71 | Arrays.asList("GL_EXT_debug_marker", "GL_EXT_discard_framebuffer", "GL_EXT_multi_draw_arrays", 72 | "GL_EXT_shader_texture_lod", "GL_EXT_texture_format_BGRA8888", 73 | "GL_IMG_multisampled_render_to_texture", "GL_IMG_program_binary", 74 | "GL_IMG_read_format", "GL_IMG_shader_binary", "GL_IMG_texture_compression_pvrtc", 75 | "GL_IMG_texture_format_BGRA8888", "GL_IMG_texture_npot", 76 | "GL_IMG_vertex_array_object", "GL_OES_EGL_image", "GL_OES_EGL_image_external", 77 | "GL_OES_blend_equation_separate", "GL_OES_blend_func_separate", 78 | "GL_OES_blend_subtract", "GL_OES_byte_coordinates", 79 | "GL_OES_compressed_ETC1_RGB8_texture", "GL_OES_compressed_paletted_texture", 80 | "GL_OES_depth24", "GL_OES_depth_texture", "GL_OES_draw_texture", 81 | "GL_OES_egl_sync", "GL_OES_element_index_uint", "GL_OES_extended_matrix_palette", 82 | "GL_OES_fixed_point", "GL_OES_fragment_precision_high", 83 | "GL_OES_framebuffer_object", "GL_OES_get_program_binary", "GL_OES_mapbuffer", 84 | "GL_OES_matrix_get", "GL_OES_matrix_palette", "GL_OES_packed_depth_stencil", 85 | "GL_OES_point_size_array", "GL_OES_point_sprite", "GL_OES_query_matrix", 86 | "GL_OES_read_format", "GL_OES_required_internalformat", "GL_OES_rgb8_rgba8", 87 | "GL_OES_single_precision", "GL_OES_standard_derivatives", "GL_OES_stencil8", 88 | "GL_OES_stencil_wrap", "GL_OES_texture_cube_map", "GL_OES_texture_env_crossbar", 89 | "GL_OES_texture_float", "GL_OES_texture_half_float", 90 | "GL_OES_texture_mirrored_repeat", "GL_OES_vertex_array_object", 91 | "GL_OES_vertex_half_float")); 92 | 93 | } 94 | 95 | public String getBuildId() { 96 | return getString(KEY_BUILD_ID); 97 | } 98 | 99 | public AndroidContext buildId(String id) { 100 | return set(KEY_BUILD_ID, id); 101 | } 102 | 103 | private static String randomImei() { 104 | final StringBuilder sb = new StringBuilder(15); 105 | for (int i = 0; i < 15; i++) { 106 | sb.append("0123456789".charAt(rand.nextInt(10))); 107 | } 108 | return sb.toString(); 109 | } 110 | 111 | private static String randomMacAddress() { 112 | String mac = "b407f9"; 113 | for (int i = 0; i < 6; i++) { 114 | mac += Integer.toString(rand.nextInt(16), 16); 115 | } 116 | return mac; 117 | } 118 | 119 | private static String randomMeid() { 120 | // http://en.wikipedia.org/wiki/International_Mobile_Equipment_Identity 121 | // We start with a known base, and generate random MEID 122 | String meid = "35503104"; 123 | for (int i = 0; i < 6; i++) { 124 | meid += Integer.toString(rand.nextInt(10)); 125 | } 126 | 127 | // Luhn algorithm (check digit) 128 | int sum = 0; 129 | for (int i = 0; i < meid.length(); i++) { 130 | int c = Integer.parseInt(String.valueOf(meid.charAt(i))); 131 | if ((meid.length() - i - 1) % 2 == 0) { 132 | c *= 2; 133 | c = c % 10 + c / 10; 134 | } 135 | 136 | sum += c; 137 | } 138 | final int check = (100 - sum) % 10; 139 | meid += Integer.toString(check); 140 | 141 | return meid; 142 | } 143 | 144 | private static String randomSerialNumber() { 145 | String serial = "3933E6"; 146 | for (int i = 0; i < 10; i++) { 147 | serial += Integer.toString(rand.nextInt(16), 16); 148 | } 149 | serial = serial.toUpperCase(); 150 | return serial; 151 | } 152 | 153 | public Long getAndroidId() { 154 | return getLong(KEY_ANDROID_ID_LONG); 155 | } 156 | 157 | public String getAndroidIdHex() { 158 | return getString(KEY_ANDROID_ID_HEX); 159 | } 160 | 161 | public String getBuildDevice() { 162 | return getString(KEY_BUILD_DEVICE); 163 | } 164 | 165 | public String getDigest() { 166 | return getString(KEY_CHECKIN_DIGEST); 167 | } 168 | 169 | public String getEmail() { 170 | return getString(KEY_EMAIL); 171 | } 172 | 173 | public String getMacAddress() { 174 | return getString(KEY_WIFI_MAC_ADDRESS); 175 | } 176 | 177 | public int getSdkVersion() { 178 | return getInt(KEY_SDK_VERSION); 179 | } 180 | 181 | public long getSecurityToken() { 182 | return getLong(KEY_CHECKIN_SECURITY_TOKEN); 183 | } 184 | 185 | public String getSimOperator() { 186 | return getString(KEY_SIM_OPERATOR_NUMERIC); 187 | } 188 | 189 | public AndroidContext userNumber(int userNumber) { 190 | set(KEY_USER_NUMBER, userNumber); 191 | return this; 192 | } 193 | 194 | public AndroidContext setAndroidId(final long androidId) { 195 | set(KEY_ANDROID_ID_HEX, Long.toHexString(androidId)); 196 | set(KEY_ANDROID_ID_LONG, androidId); 197 | return this; 198 | } 199 | 200 | public AndroidContext setAndroidId(final String androidId) { 201 | set(KEY_ANDROID_ID_LONG, Long.parseLong(androidId, 16)); 202 | set(KEY_ANDROID_ID_HEX, androidId); 203 | return this; 204 | } 205 | 206 | public AndroidContext setBuildBootloader(final String bootloader) { 207 | return set(KEY_BUILD_BOOTLOADER, bootloader); 208 | } 209 | 210 | public AndroidContext buildBrand(final String brand) { 211 | return set(KEY_BUILD_BRAND, brand); 212 | } 213 | 214 | public AndroidContext buildClientId(final String client) { 215 | set(KEY_CLIENT_ID, client); 216 | return this; 217 | } 218 | 219 | public AndroidContext buildDevice(final String device) { 220 | return set(KEY_BUILD_DEVICE, device); 221 | } 222 | 223 | public AndroidContext buildFingerprint(final String buildId) { 224 | return set(KEY_BUILD_FINGERPRINT, buildId); 225 | } 226 | 227 | public AndroidContext buildHardware(final String buildHardware) { 228 | return set(KEY_BUILD_HARDWARE, buildHardware); 229 | } 230 | 231 | public AndroidContext buildManufacturer(final String manufacturer) { 232 | return set(KEY_BUILD_MANUFACTURER, manufacturer); 233 | } 234 | 235 | public AndroidContext buildModel(final String model) { 236 | return set(KEY_BUILD_MODEL, model); 237 | } 238 | 239 | public AndroidContext otaInstalled(final boolean buildOtaInstalled) { 240 | return set(KEY_OTA_INSTALLED, buildOtaInstalled); 241 | } 242 | 243 | public AndroidContext buildProduct(final String product) { 244 | return set(KEY_BUILD_PRODUCT, product); 245 | } 246 | 247 | public AndroidContext setBuildRadio(final String radio) { 248 | return set(KEY_BUILD_RADIO, radio); 249 | } 250 | 251 | public AndroidContext sdkVersion(final int sdkVersion) { 252 | return set(KEY_SDK_VERSION, sdkVersion); 253 | } 254 | 255 | public AndroidContext setBuildSerial(final String buildSerial) { 256 | return set(KEY_BUILD_SERIAL, buildSerial); 257 | } 258 | 259 | public AndroidContext buildTime(final long buildTimestamp) { 260 | return set(KEY_BUILD_TIME, buildTimestamp); 261 | } 262 | 263 | public AndroidContext setCellOperator(final String numeric) { 264 | return set(KEY_CELL_OPERATOR_NUMERIC, numeric); 265 | } 266 | 267 | public AndroidContext setCellOperator(final String numeric, final String alpha) { 268 | setCellOperatorName(alpha); 269 | return setCellOperator(numeric); 270 | } 271 | 272 | public AndroidContext setCellOperatorName(final String alpha) { 273 | return set(KEY_CELL_OPERATOR_NAME, alpha); 274 | } 275 | 276 | public AndroidContext setDeviceDensityDpi(final int deviceDensityDpi) { 277 | return set(KEY_DEVICE_SCREEN_DPI, deviceDensityDpi); 278 | } 279 | 280 | public AndroidContext deviceFeatures(final List systemFeatures) { 281 | return set(KEY_DEVICE_FEATURES, systemFeatures); 282 | } 283 | 284 | public AndroidContext deviceGlEsVersion(final int deviceGlEsVersion) { 285 | return set(KEY_DEVICE_GLES_VERSION, deviceGlEsVersion); 286 | } 287 | 288 | public AndroidContext deviceHeightPixels(final int deviceHeightPixels) { 289 | return set(KEY_DEVICE_SCREEN_HEIGHT_PX, deviceHeightPixels); 290 | } 291 | 292 | public AndroidContext deviceKeyboardType(final int deviceKeyboardType) { 293 | return set(KEY_DEVICE_KEYBOARD_TYPE, deviceKeyboardType); 294 | } 295 | 296 | public AndroidContext deviceLocales(final List supportedLocales) { 297 | return set(KEY_DEVICE_LOCALES, supportedLocales); 298 | } 299 | 300 | public AndroidContext deviceNavigation(final int deviceNavigation) { 301 | return set(KEY_DEVICE_NAVIGATION, deviceNavigation); 302 | } 303 | 304 | public AndroidContext deviceScreenLayout(final int deviceScreenLayout) { 305 | return set(KEY_DEVICE_SCREEN_LAYOUT, deviceScreenLayout); 306 | } 307 | 308 | public AndroidContext deviceSharedLibraries(final List sharedLibraries) { 309 | return set(KEY_DEVICE_SHARED_LIBRARY, sharedLibraries); 310 | } 311 | 312 | public AndroidContext deviceTouchScreen(final int deviceTouchScreen) { 313 | return set(KEY_DEVICE_TOUCH_SCREEN, deviceTouchScreen); 314 | } 315 | 316 | public AndroidContext deviceWidthPixels(final int deviceWidthPixels) { 317 | return set(KEY_DEVICE_SCREEN_WIDTH_PX, deviceWidthPixels); 318 | } 319 | 320 | public AndroidContext setDigest(final String digest) { 321 | return set(KEY_CHECKIN_DIGEST, digest); 322 | } 323 | 324 | public AndroidContext setEmail(final String email) { 325 | return set(KEY_EMAIL, email); 326 | } 327 | 328 | public AndroidContext setEsn(final String esn) { 329 | return set(KEY_ESN, esn); 330 | } 331 | 332 | public AndroidContext glExtensions(final List glExtensions) { 333 | return set(KEY_DEVICE_GLES_EXTENSIONS, glExtensions); 334 | } 335 | 336 | public AndroidContext imei(final String imei) { 337 | return set(KEY_IMEI, imei); 338 | } 339 | 340 | public AndroidContext locale(final Locale locale) { 341 | return set(KEY_LOCALE, locale.getLanguage().toLowerCase()+"_"+locale.getCountry().toUpperCase()); 342 | } 343 | 344 | public AndroidContext loggingId(final long loggingId) { 345 | set(KEY_LOGGING_ID, loggingId); 346 | return this; 347 | } 348 | 349 | public AndroidContext macAddress(final String mac) { 350 | return set(KEY_WIFI_MAC_ADDRESS, mac); 351 | } 352 | 353 | public AndroidContext meid(final String meid) { 354 | return set(KEY_MEID, meid); 355 | } 356 | 357 | public AndroidContext operator(final String numeric, final String alpha) { 358 | setCellOperator(numeric, alpha); 359 | simOperator(numeric, alpha); 360 | return this; 361 | } 362 | 363 | public AndroidContext otaCert(final String otaCert) { 364 | return set(KEY_OTA_CERT, otaCert); 365 | } 366 | 367 | public AndroidContext roaming(final String roaming) { 368 | return set(KEY_ROAMING, roaming); 369 | } 370 | 371 | public AndroidContext securityToken(final long securityToken) { 372 | return set(KEY_CHECKIN_SECURITY_TOKEN, securityToken); 373 | } 374 | 375 | public AndroidContext serial(final String serial) { 376 | return set(KEY_BUILD_SERIAL, serial); 377 | } 378 | 379 | public AndroidContext simOperator(final String operatorNumeric) { 380 | return set(KEY_SIM_OPERATOR_NUMERIC, operatorNumeric); 381 | } 382 | 383 | public AndroidContext simOperator(final String numeric, final String alpha) { 384 | simOperatorName(alpha); 385 | simOperator(numeric); 386 | return this; 387 | } 388 | 389 | public AndroidContext simOperatorName(final String operatorAlpha) { 390 | return set(KEY_SIM_OPERATOR_NAME, operatorAlpha); 391 | } 392 | 393 | public AndroidContext timeZone(final String timeZone) { 394 | return set(KEY_TIME_ZONE, timeZone); 395 | } 396 | } 397 | -------------------------------------------------------------------------------- /src/com/google/android/AndroidRequestKeys.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 μg Project Team 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.android; 18 | 19 | public class AndroidRequestKeys { 20 | public static interface BuildMetrics { 21 | public static final String KEY_BUILD_BOOTLOADER = "buildBootloader"; 22 | public static final String KEY_BUILD_BRAND = "buildBrand"; 23 | public static final String KEY_BUILD_DEVICE = "buildDevice"; 24 | public static final String KEY_BUILD_FINGERPRINT = "buildFingerprint"; 25 | public static final String KEY_BUILD_HARDWARE = "buildHardware"; 26 | public static final String KEY_BUILD_MANUFACTURER = "buildManufacturer"; 27 | public static final String KEY_BUILD_MODEL = "buildModel"; 28 | public static final String KEY_BUILD_PRODUCT = "buildProduct"; 29 | public static final String KEY_BUILD_RADIO = "buildRadio"; 30 | public static final String KEY_BUILD_SERIAL = "buildSerial"; 31 | public static final String KEY_BUILD_ID = "buildId"; 32 | public static final String KEY_BUILD_TIME = "buildTime"; 33 | 34 | public static final String KEY_CLIENT_ID = "clientId"; 35 | public static final String KEY_OTA_INSTALLED = "otaInstalled"; 36 | public static final String KEY_OTA_CERT = "otaCert"; 37 | public static final String KEY_SDK_VERSION = "sdkVersion"; 38 | } 39 | 40 | public static interface DeviceMetrics { 41 | public static final String KEY_DEVICE_FEATURES = "deviceFeatures"; 42 | public static final String KEY_DEVICE_SCREEN_DPI = "deviceScreenDpi"; 43 | public static final String KEY_DEVICE_CLASS = "deviceClass"; 44 | public static final String KEY_DEVICE_GLES_VERSION = "deviceGlEsVersion"; 45 | public static final String KEY_DEVICE_GLES_EXTENSIONS = "deviceGlEsExtensions"; 46 | public static final String KEY_DEVICE_FIVE_WAY_NAVIGATION = "deviceFiveWayNavigation"; 47 | public static final String KEY_DEVICE_HARD_KEYBOARD = "deviceHardKeyboard"; 48 | public static final String KEY_DEVICE_SCREEN_HEIGHT_PX = "deviceScreenHeightPx"; 49 | public static final String KEY_DEVICE_KEYBOARD_TYPE = "deviceKeyboardType"; 50 | public static final String KEY_DEVICE_LOCALES = "deviceLocales"; 51 | public static final String KEY_DEVICE_NATIVE_PLATFORM = "deviceNativePlatform"; 52 | public static final String KEY_DEVICE_NAVIGATION = "deviceNavigation"; 53 | public static final String KEY_DEVICE_SCREEN_LAYOUT = "deviceScreenLayout"; 54 | public static final String KEY_DEVICE_SHARED_LIBRARY = "deviceSharedLibrary"; 55 | public static final String KEY_DEVICE_TOUCH_SCREEN = "deviceTouchscreen"; 56 | public static final String KEY_DEVICE_SCREEN_WIDTH_PX = "deviceScreenWidthPx"; 57 | } 58 | 59 | public static interface OperatorMetrics { 60 | public static final String KEY_CELL_OPERATOR_NAME = "cellOperatorName"; 61 | public static final String KEY_CELL_OPERATOR_NUMERIC = "cellOperatorNumeric"; 62 | public static final String KEY_SIM_OPERATOR_NAME = "simOperatorName"; 63 | public static final String KEY_SIM_OPERATOR_NUMERIC = "simOperatorNumeric"; 64 | public static final String KEY_ROAMING = "roaming"; 65 | } 66 | 67 | public static interface UserMetrics { 68 | public static final String KEY_LOCALE = "locale"; 69 | public static final String KEY_TIME_ZONE = "timeZone"; 70 | public static final String KEY_CHECKIN_DIGEST = "checkinDigest"; 71 | } 72 | 73 | public static interface DeviceIdentifiers { 74 | public static final String KEY_ANDROID_ID_LONG = "androidIdLong"; 75 | public static final String KEY_ANDROID_ID_HEX = "androidIdHex"; 76 | public static final String KEY_LOGGING_ID = "loggingId"; 77 | public static final String KEY_WIFI_MAC_ADDRESS = "wifiMacAddress"; 78 | public static final String KEY_MEID = "meid"; 79 | public static final String KEY_ESN = "esn"; 80 | public static final String KEY_IMEI = "imei"; 81 | public static final String KEY_CHECKIN_SECURITY_TOKEN = "checkinSecurityToken"; 82 | } 83 | 84 | public static interface UserIdentifiers { 85 | public static final String KEY_AUTHORIZATION_TOKEN = "authToken"; 86 | public static final String KEY_EMAIL = "email"; 87 | public static final String KEY_USER_NUMBER = "userNumber"; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/com/google/auth/AuthClient.java: -------------------------------------------------------------------------------- 1 | package com.google.auth; 2 | 3 | import com.google.tools.Client; 4 | 5 | import java.io.IOException; 6 | import java.net.HttpURLConnection; 7 | import java.net.URL; 8 | 9 | public class AuthClient extends Client { 10 | 11 | private DataMap sendData(final URL url, final DataMapReader data, final String userAgent) { 12 | return DataMap.fromUrlDataString(sendString(url, data.toString(), userAgent)); 13 | 14 | } 15 | 16 | public AuthResponse sendRequest(final AuthRequest request) { 17 | return new AuthResponse(sendData(request.getRequestUrl(), request, request.getUserAgent())); 18 | } 19 | 20 | private String sendString(final HttpURLConnection connection, final String dataString) { 21 | prepareConnection(connection, false); 22 | writeData(connection, dataString, false); 23 | return new String(readData(connection, isError(connection), false)); 24 | } 25 | 26 | private String sendString(final URL url, final String dataString, final String userAgent) { 27 | HttpURLConnection connection = null; 28 | try { 29 | connection = (HttpURLConnection) url.openConnection(); 30 | setUserAgent(connection, userAgent); 31 | return sendString(connection, dataString); 32 | } catch (final IOException e) { 33 | if (DEBUG) { 34 | System.err.println("Could not open Connection!"); 35 | } 36 | throw new RuntimeException("Could not open Connection!", e); 37 | } finally { 38 | if (connection != null) { 39 | connection.disconnect(); 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/com/google/auth/AuthCrypto.java: -------------------------------------------------------------------------------- 1 | package com.google.auth; 2 | 3 | import com.google.tools.Base64; 4 | 5 | import javax.crypto.Cipher; 6 | import java.math.BigInteger; 7 | import java.security.KeyFactory; 8 | import java.security.MessageDigest; 9 | import java.security.PublicKey; 10 | import java.security.spec.RSAPublicKeySpec; 11 | 12 | public class AuthCrypto { 13 | private static final String CHARENC_UTF8 = "UTF-8"; 14 | private static final String CRYPT_CIPHER = "RSA/ECB/OAEPWITHSHA1ANDMGF1PADDING"; 15 | private static final String CRYPT_DIVIDER = "\uFFFD\uFFFD"; 16 | private static final String CRYPT_HASH_TYPE = "SHA-1"; 17 | private static final String CRYPT_PUBLIC_KEY = 18 | "AAAAgMom/1a/v0lblO2Ubrt60J2gcuXSljGFQXgcyZWveWLEwo6prwgi3iJIZdodyhKZQrNWp5nKJ3srRXcUW+F1BD3baEVGcmEgqaLZUNBjm057pKRI16kB0YppeGx5qIQ5QjKzsR8ETQbKLNWgRY0QRNVz34kMJR3P/LgHax/6rmf5AAAAAwEAAQ=="; 19 | private static final String CRYPT_TYPE = "RSA"; 20 | 21 | private static PublicKey createKey(final String keyStr, final byte ciphertextHeader[]) { 22 | try { 23 | final byte data[] = Base64.decode(keyStr, 0); 24 | 25 | final int modulusLength = readInt(data, 0); 26 | byte temp[] = new byte[modulusLength]; 27 | System.arraycopy(data, 4, temp, 0, modulusLength); 28 | final BigInteger modulus = new BigInteger(1, temp); 29 | 30 | final int exponentLength = readInt(data, modulusLength + 4); 31 | temp = new byte[exponentLength]; 32 | System.arraycopy(data, modulusLength + 8, temp, 0, exponentLength); 33 | final BigInteger exponent = new BigInteger(1, temp); 34 | 35 | final MessageDigest sha1 = MessageDigest.getInstance(CRYPT_HASH_TYPE); 36 | final byte hash[] = sha1.digest(data); 37 | ciphertextHeader[0] = 0; 38 | System.arraycopy(hash, 0, ciphertextHeader, 1, 4); 39 | 40 | return KeyFactory.getInstance(CRYPT_TYPE).generatePublic(new RSAPublicKeySpec(modulus, exponent)); 41 | } catch (final Throwable t) { 42 | throw new RuntimeException(t); 43 | } 44 | 45 | } 46 | 47 | public static String encryptPassword(final String email, final String password) { 48 | return encryptPassword(email, password, CRYPT_PUBLIC_KEY); 49 | } 50 | 51 | private static String encryptPassword(final String email, final String password, final String publicKeyData) { 52 | final String combined = email + CRYPT_DIVIDER + password; 53 | return encryptString(combined, publicKeyData); 54 | } 55 | 56 | private static String encryptString(final String plain, final String publicKeyData) { 57 | // TODO Something seems to be non-working... 58 | if (publicKeyData == null || publicKeyData.isEmpty()) { 59 | return null; 60 | } 61 | final byte[] ciphertextHeader = new byte[5]; 62 | final PublicKey publicKey = createKey(publicKeyData, ciphertextHeader); 63 | if (publicKey == null) { 64 | return null; 65 | } 66 | try { 67 | final Cipher cipher = Cipher.getInstance(CRYPT_CIPHER); 68 | final byte[] plainbytes = plain.getBytes(CHARENC_UTF8); 69 | final int chunks = 1 + (plainbytes.length - 1) / 86; 70 | final byte[] bytes = new byte[chunks * 133]; 71 | for (int i = 0; i < chunks; i++) { 72 | cipher.init(Cipher.ENCRYPT_MODE, publicKey); 73 | final int start = i * 86; 74 | int length = 86; 75 | if (i == chunks - 1) { 76 | length = plainbytes.length - start; 77 | } 78 | final byte[] cipherbytes = cipher.doFinal(plainbytes, start, length); 79 | System.arraycopy(ciphertextHeader, 0, bytes, i * 133, ciphertextHeader.length); 80 | System.arraycopy(cipherbytes, 0, bytes, i * 133 + ciphertextHeader.length, cipherbytes.length); 81 | } 82 | return Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP); 83 | } catch (final Throwable t) { 84 | } 85 | return null; 86 | } 87 | 88 | private static int readInt(final byte src[], final int offset) { 89 | int i = 0 | (0xff & src[offset]) << 24; 90 | i |= (0xff & src[offset + 1]) << 16; 91 | i |= (0xff & src[offset + 2]) << 8; 92 | i |= 0xff & src[offset + 3]; 93 | return i; 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/com/google/auth/AuthRequest.java: -------------------------------------------------------------------------------- 1 | package com.google.auth; 2 | 3 | import java.net.URL; 4 | 5 | public class AuthRequest extends DataMapWriter { 6 | 7 | private static final String REQUEST_URL_DEFAULT_GOOGLE_AUTH = "https://www.google.com/accounts/ClientLogin"; 8 | 9 | private URL requestUrl; 10 | private String userAgent; 11 | 12 | public AuthRequest() { 13 | super(); 14 | } 15 | 16 | public AuthRequest(final URL requestUrl, final DataMap dataMap) { 17 | super(dataMap); 18 | setRequestUrl(requestUrl); 19 | } 20 | 21 | public URL getRequestUrl() { 22 | return requestUrl; 23 | } 24 | 25 | public String getUserAgent() { 26 | return userAgent; 27 | } 28 | 29 | @Override 30 | public void recycle() { 31 | super.recycle(); 32 | setRequestUrl(REQUEST_URL_DEFAULT_GOOGLE_AUTH); 33 | } 34 | 35 | public AuthResponse send() { 36 | return new AuthClient().sendRequest(this); 37 | } 38 | 39 | public void setUserAgent(String userAgent) { 40 | this.userAgent = userAgent; 41 | } 42 | 43 | public void setRequestUrl(final String requestUrl) { 44 | if (requestUrl == null || requestUrl.isEmpty()) { 45 | throw new RuntimeException("RequestUrl should not be empty!"); 46 | } 47 | try { 48 | setRequestUrl(new URL(requestUrl)); 49 | } catch (final Exception e) { 50 | throw new RuntimeException("RequestUrl should be a valid URL!", e); 51 | } 52 | } 53 | 54 | public void setRequestUrl(final URL requestUrl) { 55 | if (requestUrl == null) { 56 | throw new RuntimeException("RequestUrl should not be null!"); 57 | } 58 | this.requestUrl = requestUrl; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/com/google/auth/AuthResponse.java: -------------------------------------------------------------------------------- 1 | package com.google.auth; 2 | 3 | public class AuthResponse extends DataMapReader { 4 | 5 | public AuthResponse(final DataMap dataMap) { 6 | super(dataMap); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/com/google/auth/AuthType.java: -------------------------------------------------------------------------------- 1 | package com.google.auth; 2 | 3 | public enum AuthType { 4 | AuthToken, EncryptedPassword, MasterToken, Password; 5 | 6 | public static AuthType fromInt(final int i) { 7 | switch (i) { 8 | case 1: 9 | return AuthToken; 10 | case 2: 11 | return EncryptedPassword; 12 | case 4: 13 | return Password; 14 | case 3: 15 | default: 16 | return MasterToken; 17 | } 18 | } 19 | 20 | public int toInt() { 21 | switch (this) { 22 | case AuthToken: 23 | return 1; 24 | case EncryptedPassword: 25 | return 2; 26 | case MasterToken: 27 | return 3; 28 | case Password: 29 | return 4; 30 | default: 31 | return 0; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/com/google/auth/DataField.java: -------------------------------------------------------------------------------- 1 | package com.google.auth; 2 | 3 | import java.util.HashMap; 4 | 5 | public enum DataField { 6 | ACCOUNT_TYPE("accountType"), ADDED_ACCOUNT("add_account"), ANDROID_ID("androidId"), AUTH_TOKEN("Auth"), 7 | CAN_UPGRADE_PLUS("GooglePlusUpgrade"), CAPTCHA_ANSWER("logincaptcha"), CAPTCHA_BITMAP("captchaBitmap"), 8 | CAPTCHA_DATA("CaptchaData"), CAPTCHA_TOKEN("logintoken"), CAPTCHA_TOKEN_RES("CaptchaToken"), 9 | CAPTCHA_URL("CaptchaUrl"), CLIENT_ID("client_id"), CREATED("created"), DETAIL("ErrorDetail"), 10 | DEVICE_COUNTRY("device_country"), EMAIL("Email"), ENCRYPTED_PASSWORD("EncryptedPasswd"), EXPIRY("Expiry"), 11 | FIRST_NAME("firstName"), GENDER("gender"), GPLUS_CHECK("gplus_check"), ID("id"), INFO("Info"), 12 | JSON_STATUS("status"), LANGUAGE("lang"), LAST_NAME("lastName"), LSID("LSID"), MASTER_TOKEN("Token"), 13 | NEEDS_CREDIT_CARD("CC"), OAUTH2_EXTRA_PREFIX("oauth2_"), OPERATOR_COUNTRY("operatorCountry"), PACKAGE_NAME("app"), 14 | PACKAGE_SIGNATURE("client_sig"), PASSWORD("Passwd"), PERMISSION("Permission"), PERMISSION_ADVICE("issueAdvice"), 15 | PHOTO("photo"), PICASA_USER("PicasaUser"), SDK_VERSION("sdk_version"), SECURITY_TOKEN("secTok"), SERVICE("service"), 16 | SERVICE_GPLUS("googleme"), SERVICE_HOSTED("HOSTED"), SERVICES("services"), SID("SID"), SOURCE("source"), 17 | STATUS("Error"), STORED_PERMISSION("has_permission"), TIME_STAMP("timeStmp"), URL("Url"), 18 | YOUTUBE_USER("YouTubeUser"); 19 | 20 | private static HashMap internalMap; 21 | 22 | public static DataField fromInternalName(final String internalName) { 23 | return internalMap.get(internalName); 24 | } 25 | 26 | private final String internalName; 27 | 28 | private DataField(final String internalName) { 29 | this.internalName = internalName; 30 | saveToMap(); 31 | } 32 | 33 | private void saveToMap() { 34 | if (internalMap == null) { 35 | internalMap = new HashMap(); 36 | } 37 | internalMap.put(internalName, this); 38 | } 39 | 40 | public String toEnumName() { 41 | return super.toString(); 42 | } 43 | 44 | public String toInternalName() { 45 | return internalName; 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return internalName; 51 | } 52 | } -------------------------------------------------------------------------------- /src/com/google/auth/DataMap.java: -------------------------------------------------------------------------------- 1 | package com.google.auth; 2 | 3 | import com.google.tools.Client; 4 | 5 | import java.io.UnsupportedEncodingException; 6 | import java.net.URLDecoder; 7 | import java.net.URLEncoder; 8 | import java.util.EnumMap; 9 | 10 | public class DataMap extends EnumMap { 11 | 12 | private static final String CHARENC_UTF8 = "UTF-8"; 13 | private static final long serialVersionUID = 7378197191935938976L; 14 | 15 | public static DataMap fromUrlDataString(final String string) { 16 | final String[] parts = string.split("\n"); 17 | final DataMap dataMap = new DataMap(); 18 | for (final String part : parts) { 19 | final int split = part.indexOf("="); 20 | if (split > 0) { 21 | try { 22 | final String key = URLDecoder.decode(part.substring(0, split), CHARENC_UTF8); 23 | final String value = URLDecoder.decode(part.substring(split + 1), CHARENC_UTF8); 24 | dataMap.put(DataField.fromInternalName(key), value); 25 | } catch (final Throwable t) { 26 | if (Client.DEBUG) { 27 | System.err.println("Could not decode: " + part); 28 | } 29 | } 30 | } else { 31 | if (Client.DEBUG) { 32 | System.err.println("Not a key value pair: " + part); 33 | 34 | } 35 | } 36 | } 37 | return dataMap; 38 | } 39 | 40 | public DataMap() { 41 | super(DataField.class); 42 | } 43 | 44 | public DataMap(final EnumMap clone) { 45 | super(clone); 46 | } 47 | 48 | @Override 49 | public DataMap clone() { 50 | return new DataMap(super.clone()); 51 | } 52 | 53 | public String getUrlDataString() { 54 | final StringBuilder builder = new StringBuilder(); 55 | for (final DataField field : keySet()) { 56 | String value = get(field); 57 | if (value != null && !value.isEmpty()) { 58 | try { 59 | final String key = URLEncoder.encode(field.toInternalName(), CHARENC_UTF8); 60 | value = URLEncoder.encode(value, CHARENC_UTF8); 61 | builder.append(key).append("=").append(value).append("&"); 62 | } catch (final UnsupportedEncodingException e) { 63 | // Ignore and go next 64 | } 65 | } 66 | } 67 | if (builder.length() == 0) { 68 | return ""; 69 | } 70 | return builder.substring(0, builder.length() - 1); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/com/google/auth/DataMapReader.java: -------------------------------------------------------------------------------- 1 | package com.google.auth; 2 | 3 | import java.util.Set; 4 | 5 | public class DataMapReader { 6 | 7 | protected DataMap dataMap; 8 | 9 | public DataMapReader(final DataMap dataMap) { 10 | if (dataMap == null) { 11 | throw new RuntimeException("dataMap should not be null!"); 12 | } 13 | this.dataMap = dataMap; 14 | } 15 | 16 | public String getData(final DataField field) { 17 | return dataMap.get(field); 18 | } 19 | 20 | public Set getKeys() { 21 | return dataMap.keySet(); 22 | } 23 | 24 | @Override 25 | public String toString() { 26 | return dataMap.getUrlDataString(); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/com/google/auth/DataMapWriter.java: -------------------------------------------------------------------------------- 1 | package com.google.auth; 2 | 3 | public class DataMapWriter extends DataMapReader { 4 | 5 | public DataMapWriter() { 6 | this(new DataMap()); 7 | recycle(); 8 | } 9 | 10 | public DataMapWriter(final DataMap dataMap) { 11 | super(dataMap); 12 | } 13 | 14 | public String putData(final DataField field, final String data) { 15 | return dataMap.put(field, data); 16 | } 17 | 18 | public void putData(final DataMapReader data) { 19 | for (final DataField field : data.getKeys()) { 20 | final String value = data.getData(field); 21 | if (value != null && !value.isEmpty()) { 22 | putData(field, value); 23 | } 24 | } 25 | 26 | } 27 | 28 | public void recycle() { 29 | dataMap = new DataMap(); 30 | } 31 | 32 | public void setDataMap(final DataMap dataMap) { 33 | if (dataMap == null) { 34 | throw new RuntimeException("dataMap should not be null!"); 35 | } 36 | this.dataMap = dataMap; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/com/google/c2dm/C2DMClient.java: -------------------------------------------------------------------------------- 1 | package com.google.c2dm; 2 | 3 | import com.google.android.AndroidContext; 4 | import com.google.android.AndroidRequestKeys; 5 | import com.google.tools.Client; 6 | import com.google.tools.RequestContext; 7 | 8 | import java.io.IOException; 9 | import java.net.HttpURLConnection; 10 | import java.net.URL; 11 | import java.net.URLEncoder; 12 | import java.util.Map; 13 | 14 | public class C2DMClient extends Client implements 15 | AndroidRequestKeys.DeviceIdentifiers, AndroidRequestKeys.OperatorMetrics, 16 | AndroidRequestKeys.UserIdentifiers, AndroidRequestKeys.BuildMetrics { 17 | private static final String REGISTER_URL = "https://android.clients.google.com/c2dm/register3"; 18 | 19 | public static final String KEY_SMALEST_SCREEN_WIDTH_DP = "smalestScreenWidthDp"; 20 | public static final String KEY_FILTER_LEVEL = "filterLevel"; 21 | protected static final String REQUEST_CONTENT_TYPE_FORM = "application/x-www-form-urlencoded; charset=UTF-8"; 22 | 23 | protected RequestContext context; 24 | 25 | public C2DMClient(RequestContext context) { 26 | this.context = context; 27 | } 28 | 29 | 30 | protected void prepareConnection(HttpURLConnection connection, String postType) { 31 | if (postType != null) { 32 | super.prepareConnection(connection, false); 33 | connection.setRequestProperty(REQUEST_CONTENT_TYPE_FIELD, postType); 34 | } else { 35 | connection.setUseCaches(false); 36 | connection.setDoInput(true); 37 | } 38 | connection.setRequestProperty("X-DFE-MCCMNC", context.getString(KEY_CELL_OPERATOR_NUMERIC)); 39 | connection.setRequestProperty("Authorization", "GoogleLogin auth=" + context.getString(KEY_AUTHORIZATION_TOKEN)); 40 | connection.setRequestProperty("X-DFE-Device-Id", context.getString(KEY_ANDROID_ID_HEX)); 41 | connection.setRequestProperty("X-DFE-Client-Id", "am-android-google"); 42 | connection.setRequestProperty("X-DFE-Logging-Id", context.getString(KEY_LOGGING_ID)); 43 | connection.setRequestProperty("X-DFE-SmallestScreenWidthDp", context.getString(KEY_SMALEST_SCREEN_WIDTH_DP)); 44 | connection.setRequestProperty("X-DFE-Filter-Level", context.getString(KEY_FILTER_LEVEL)); 45 | connection.setRequestProperty("Accept-Language", context.get(AndroidContext.KEY_LOCALE, "en_US").replace("_", "-")); 46 | setUserAgent(connection, context); 47 | } 48 | 49 | public String registerC2DM(String app, String appCert, String sender, Map extras) { 50 | try { 51 | HttpURLConnection connection = (HttpURLConnection) new URL(REGISTER_URL).openConnection(); 52 | prepareConnection(connection, REQUEST_CONTENT_TYPE_FORM); 53 | //connection.setRequestProperty("app", app); 54 | if (extras != null) { 55 | for (String key : extras.keySet()) { 56 | connection.setRequestProperty("X-" + key, extras.get(key)); 57 | } 58 | } 59 | String send = "app=" + app + "&sender=" + URLEncoder.encode(sender) + "&device=" + context.getLong(KEY_ANDROID_ID_LONG) + "&device_user_id=0"; 60 | if (DEBUG) { 61 | System.out.println("C2DMregister.out: " + send); 62 | } 63 | writeData(connection, send.getBytes(), false); 64 | byte[] bytes = readData(connection, false); 65 | String result = new String(bytes); 66 | if (DEBUG) { 67 | System.out.println("C2DMregister.in: " + result); 68 | } 69 | String token = null; 70 | for (String keyval : result.split("\n")) { 71 | if (keyval.startsWith("token=")) { 72 | token = keyval.substring(6); 73 | } 74 | } 75 | return token; 76 | } catch (IOException e) { 77 | if (DEBUG_ERROR) { 78 | e.printStackTrace(); 79 | } 80 | return null; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/com/google/checkin/CheckinClient.java: -------------------------------------------------------------------------------- 1 | package com.google.checkin; 2 | 3 | import com.google.android.AndroidRequestKeys; 4 | import com.google.tools.Client; 5 | import com.google.tools.RequestContext; 6 | import com.squareup.wire.Wire; 7 | 8 | import java.net.HttpURLConnection; 9 | import java.net.URL; 10 | import java.util.Arrays; 11 | import java.util.Collections; 12 | import java.util.List; 13 | 14 | public class CheckinClient extends Client implements 15 | AndroidRequestKeys.BuildMetrics, AndroidRequestKeys.DeviceMetrics, 16 | AndroidRequestKeys.OperatorMetrics, AndroidRequestKeys.DeviceIdentifiers, 17 | AndroidRequestKeys.UserIdentifiers, AndroidRequestKeys.UserMetrics { 18 | private static final String REQUEST_CONTENT_TYPE = "application/x-protobuffer"; 19 | private static final String CHECKIN_URL = "https://android.clients.google.com/checkin"; 20 | private static final int VERSION = 3; 21 | private final Wire wire = new Wire(); 22 | 23 | public CheckinClient() { 24 | requestContentType = REQUEST_CONTENT_TYPE; 25 | } 26 | 27 | public Response sendRequest(final Request request, final String url) { 28 | 29 | byte[] bytes = null; 30 | try { 31 | HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); 32 | prepareConnection(connection, false); 33 | setUserAgent(connection, "Android-Checkin/2.0"); 34 | writeData(connection, request.toByteArray(), false); 35 | bytes = readData(connection, false); 36 | return wire.parseFrom(bytes, Response.class); 37 | } catch (final Exception e) { 38 | if (DEBUG_ERROR) { 39 | e.printStackTrace(System.err); 40 | if (bytes != null) { 41 | System.err.println(new String(bytes)); 42 | } 43 | } 44 | throw new RuntimeException(e); 45 | } 46 | 47 | } 48 | 49 | public CheckinResponse checkin(RequestContext context) { 50 | return checkin(context, context.getString(KEY_AUTHORIZATION_TOKEN)); 51 | } 52 | 53 | public CheckinResponse checkin(RequestContext context, String authToken) { 54 | Object TODO = null; 55 | long now = System.currentTimeMillis(); 56 | Request request = new Request.Builder() 57 | .accountCookie(buildAccountCookie(context.getString(KEY_EMAIL), authToken)) 58 | .androidId(context.getLong(KEY_ANDROID_ID_LONG)) 59 | .checkin(new Request.Checkin.Builder() 60 | .build(new Request.Checkin.Build.Builder() 61 | .bootloader(context.getString(KEY_BUILD_BOOTLOADER)) 62 | .brand(context.getString(KEY_BUILD_BRAND)) 63 | .clientId(context.getString(KEY_CLIENT_ID)) 64 | .device(context.getString(KEY_BUILD_DEVICE)) 65 | .fingerprint(context.getString(KEY_BUILD_FINGERPRINT)) 66 | .hardware(context.getString(KEY_BUILD_HARDWARE)) 67 | .manufacturer(context.getString(KEY_BUILD_MANUFACTURER)) 68 | .model(context.getString(KEY_BUILD_MODEL)) 69 | .otaInstalled(context.getBoolean(KEY_OTA_INSTALLED, false)) 70 | .packageVersionCode(context.getInt(KEY_SDK_VERSION)) 71 | .product(context.getString(KEY_BUILD_PRODUCT)) 72 | .radio(context.getString(KEY_BUILD_RADIO)) 73 | .sdkVersion(context.getInt(KEY_SDK_VERSION)) 74 | .time(context.getLong(KEY_BUILD_TIME, now) / 1000L) 75 | .build()) 76 | .cellOperator(context.getString(KEY_CELL_OPERATOR_NUMERIC)) 77 | .event(Arrays.asList(new Request.Checkin.Event("event_log_start", null, now))) 78 | .lastCheckinMs(0L) 79 | //.requestedGroup(TODO) 80 | .roaming(context.getString(KEY_ROAMING)) 81 | .simOperator(context.getString(KEY_SIM_OPERATOR_NUMERIC)) 82 | //.stat(TODO) 83 | .userNumber(0) 84 | .build()) 85 | //.desiredBuild(TODO) 86 | .deviceConfiguration(new Request.DeviceConfig.Builder() 87 | .availableFeature(context.get(KEY_DEVICE_FEATURES, Collections.emptyList())) 88 | .densityDpi(context.getInt(KEY_DEVICE_SCREEN_DPI)) 89 | //.deviceClass(context.getInt(KEY_DEVICE_CLASS)) 90 | .glEsVersion(context.getInt(KEY_DEVICE_GLES_VERSION)) 91 | .glExtension(context.get(KEY_DEVICE_GLES_EXTENSIONS, Collections.emptyList())) 92 | .hasFiveWayNavigation(context.getBoolean(KEY_DEVICE_FIVE_WAY_NAVIGATION, false)) 93 | .hasHardKeyboard(context.getBoolean(KEY_DEVICE_HARD_KEYBOARD, false)) 94 | .heightPixels(context.getInt(KEY_DEVICE_SCREEN_HEIGHT_PX)) 95 | .keyboardType(context.getInt(KEY_DEVICE_KEYBOARD_TYPE, 0)) 96 | .locale(context.get(KEY_DEVICE_LOCALES, Collections.emptyList())) 97 | //.maxApkDownloadSizeMb(TODO) 98 | .nativePlatform(context.get(KEY_DEVICE_NATIVE_PLATFORM, Collections.emptyList())) 99 | .navigation(context.getInt(KEY_DEVICE_NAVIGATION, 0)) 100 | .screenLayout(context.getInt(KEY_DEVICE_SCREEN_LAYOUT, 0)) 101 | .sharedLibrary(context.get(KEY_DEVICE_SHARED_LIBRARY, Collections.emptyList())) 102 | .touchScreen(context.getInt(KEY_DEVICE_TOUCH_SCREEN, 0)) 103 | .widthPixels(context.getInt(KEY_DEVICE_SCREEN_WIDTH_PX)) 104 | .build()) 105 | .digest(context.getString(KEY_CHECKIN_DIGEST)) 106 | .esn(context.getString(KEY_ESN)) 107 | .fragment(0) 108 | //.imei(context.getString(KEY_IMEI)) // Note: this is never set, use meid instead 109 | .locale(context.getString(KEY_LOCALE)) 110 | .loggingId(context.getLong(KEY_LOGGING_ID)) 111 | .macAddress(Arrays.asList(context.getString(KEY_WIFI_MAC_ADDRESS))) 112 | .macAddressType(Arrays.asList("wifi")) 113 | //.marketCheckin(TODO) 114 | .meid(context.get(KEY_IMEI, context.getString(KEY_MEID))) 115 | .otaCert(Arrays.asList(context.getString(KEY_OTA_CERT))) 116 | .securityToken(context.getLong(KEY_CHECKIN_SECURITY_TOKEN)) 117 | .serial(context.getString(KEY_BUILD_SERIAL)) 118 | .timeZone(context.getString(KEY_TIME_ZONE)) 119 | //.userName(TODO) 120 | //.userSerialNumber(TODO) 121 | .version(VERSION) 122 | .build(); 123 | 124 | if (Client.DEBUG) { 125 | System.out.println(request); 126 | } 127 | 128 | final Response response = sendRequest(request, CHECKIN_URL); 129 | if (Client.DEBUG) { 130 | System.out.println(response); 131 | } 132 | 133 | if (response == null) { 134 | return null; 135 | } else { 136 | return new CheckinResponse(response); 137 | } 138 | } 139 | 140 | private List buildAccountCookie(String email, String authToken) { 141 | if (email != null && !email.isEmpty()) { 142 | if (authToken != null && !authToken.isEmpty()) { 143 | return Arrays.asList("[" + email + "]", authToken); 144 | } else { 145 | return Arrays.asList("[" + email + "]"); 146 | } 147 | } 148 | return Arrays.asList(""); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/com/google/checkin/CheckinResponse.java: -------------------------------------------------------------------------------- 1 | package com.google.checkin; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.util.Collections; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | public class CheckinResponse { 10 | private final long androidId; 11 | private final long securityToken; 12 | private final Map settings; 13 | private final boolean marketEnabled; 14 | private final String digest; 15 | 16 | public CheckinResponse(Response response) { 17 | this(response.androidId, response.securityToken, parseSettings(response.setting), 18 | response.marketOk==null?false:response.marketOk, response.digest); 19 | } 20 | 21 | public CheckinResponse(final long androidId, final long securityToken, final Map settings, 22 | final boolean marketEnabled, final String digest) { 23 | this.androidId = androidId; 24 | this.securityToken = securityToken; 25 | this.settings = settings; 26 | this.marketEnabled = marketEnabled; 27 | this.digest = digest; 28 | } 29 | 30 | private static Map parseSettings(List settings) { 31 | Map map = new HashMap(); 32 | for (Response.GservicesSetting setting : settings) { 33 | String name = fromUtf8Bytes(setting.name.toByteArray()); 34 | String value = fromUtf8Bytes(setting.value.toByteArray()); 35 | map.put(name, value); 36 | } 37 | System.out.println(map); 38 | return Collections.unmodifiableMap(map); 39 | } 40 | 41 | private static String fromUtf8Bytes(byte[] bytes) { 42 | try { 43 | return new String(bytes, "UTF-8"); 44 | } catch (UnsupportedEncodingException e) { 45 | return null; 46 | } 47 | } 48 | 49 | public String getDigest() { 50 | return digest; 51 | } 52 | 53 | public Map getSettings() { 54 | return settings; 55 | } 56 | 57 | public boolean isMarketEnabled() { 58 | return marketEnabled; 59 | } 60 | 61 | public String getAndroidIdHex() { 62 | return Long.toHexString(androidId); 63 | } 64 | 65 | public long getAndroidId() { 66 | return androidId; 67 | } 68 | 69 | public long getSecurityToken() { 70 | return securityToken; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/com/google/checkin/Request.java: -------------------------------------------------------------------------------- 1 | // Code generated by Wire protocol buffer compiler, do not edit. 2 | // Source file: protos-repo/CheckIn.proto 3 | package com.google.checkin; 4 | 5 | import com.squareup.wire.Message; 6 | import com.squareup.wire.ProtoField; 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | import static com.squareup.wire.Message.Datatype.BOOL; 11 | import static com.squareup.wire.Message.Datatype.FIXED64; 12 | import static com.squareup.wire.Message.Datatype.FLOAT; 13 | import static com.squareup.wire.Message.Datatype.INT32; 14 | import static com.squareup.wire.Message.Datatype.INT64; 15 | import static com.squareup.wire.Message.Datatype.STRING; 16 | import static com.squareup.wire.Message.Label.REPEATED; 17 | 18 | public final class Request extends Message { 19 | 20 | public static final String DEFAULT_IMEI = ""; 21 | public static final Long DEFAULT_ANDROIDID = 0L; 22 | public static final String DEFAULT_DIGEST = ""; 23 | public static final String DEFAULT_DESIREDBUILD = ""; 24 | public static final String DEFAULT_LOCALE = ""; 25 | public static final Long DEFAULT_LOGGINGID = 0L; 26 | public static final String DEFAULT_MARKETCHECKIN = ""; 27 | public static final List DEFAULT_MACADDRESS = Collections.emptyList(); 28 | public static final String DEFAULT_MEID = ""; 29 | public static final List DEFAULT_ACCOUNTCOOKIE = Collections.emptyList(); 30 | public static final String DEFAULT_TIMEZONE = ""; 31 | public static final Long DEFAULT_SECURITYTOKEN = 0L; 32 | public static final Integer DEFAULT_VERSION = 0; 33 | public static final List DEFAULT_OTACERT = Collections.emptyList(); 34 | public static final String DEFAULT_SERIAL = ""; 35 | public static final String DEFAULT_ESN = ""; 36 | public static final List DEFAULT_MACADDRESSTYPE = Collections.emptyList(); 37 | public static final Integer DEFAULT_FRAGMENT = 0; 38 | public static final String DEFAULT_USERNAME = ""; 39 | public static final Integer DEFAULT_USERSERIALNUMBER = 0; 40 | 41 | @ProtoField(tag = 1, type = STRING) 42 | public final String imei; 43 | 44 | /** 45 | * unused 46 | */ 47 | @ProtoField(tag = 2, type = INT64) 48 | public final Long androidId; 49 | 50 | /** 51 | * Gservices["android_id"] or 0 52 | */ 53 | @ProtoField(tag = 3, type = STRING) 54 | public final String digest; 55 | 56 | /** 57 | * Gservices["digest"] or "" 58 | */ 59 | @ProtoField(tag = 4) 60 | public final Checkin checkin; 61 | 62 | @ProtoField(tag = 5, type = STRING) 63 | public final String desiredBuild; 64 | 65 | /** 66 | * unused 67 | */ 68 | @ProtoField(tag = 6, type = STRING) 69 | public final String locale; 70 | 71 | /** 72 | * Locale.toString 73 | */ 74 | @ProtoField(tag = 7, type = INT64) 75 | public final Long loggingId; 76 | 77 | /** 78 | * GoogleSettingsContract.Partner["logging_id2"] 79 | */ 80 | @ProtoField(tag = 8, type = STRING) 81 | public final String marketCheckin; 82 | 83 | /** 84 | * unused 85 | */ 86 | @ProtoField(tag = 9, type = STRING, label = REPEATED) 87 | public final List macAddress; 88 | 89 | /** 90 | * NetworkInfo.getExtraInfo, WifiInfo.getMacAddress (12 hex-digits) 91 | */ 92 | @ProtoField(tag = 10, type = STRING) 93 | public final String meid; 94 | 95 | /** 96 | * TelephonyManager.getDeviceId (14 hex-digits) 97 | */ 98 | @ProtoField(tag = 11, type = STRING, label = REPEATED) 99 | public final List accountCookie; 100 | 101 | /** 102 | * "[]" followed by "" 103 | */ 104 | @ProtoField(tag = 12, type = STRING) 105 | public final String timeZone; 106 | 107 | /** 108 | * TimeZone.getId 109 | */ 110 | @ProtoField(tag = 13, type = FIXED64) 111 | public final Long securityToken; 112 | 113 | @ProtoField(tag = 14, type = INT32) 114 | public final Integer version; 115 | 116 | /** 117 | * 3 if securityToken != 0 OR androidId == 0 118 | */ 119 | @ProtoField(tag = 15, type = STRING, label = REPEATED) 120 | public final List otaCert; 121 | 122 | /** 123 | * SHA-1 of each in /system/etc/security/otacerts.zip or "--IOException--" or "--no-output--" 124 | */ 125 | @ProtoField(tag = 16, type = STRING) 126 | public final String serial; 127 | 128 | /** 129 | * Build.SERIAL != "unknown" 130 | */ 131 | @ProtoField(tag = 17, type = STRING) 132 | public final String esn; 133 | 134 | /** 135 | * TelephonyManager.getDeviceId (8 hex-digits) 136 | */ 137 | @ProtoField(tag = 18) 138 | public final DeviceConfig deviceConfiguration; 139 | 140 | @ProtoField(tag = 19, type = STRING, label = REPEATED) 141 | public final List macAddressType; 142 | 143 | /** 144 | * "ethernet", "wifi" 145 | */ 146 | @ProtoField(tag = 20, type = INT32) 147 | public final Integer fragment; 148 | 149 | /** 150 | * unknown (use 0) 151 | */ 152 | @ProtoField(tag = 21, type = STRING) 153 | public final String userName; 154 | 155 | /** 156 | * unused 157 | */ 158 | @ProtoField(tag = 22, type = INT32) 159 | public final Integer userSerialNumber; 160 | 161 | public Request(String imei, Long androidId, String digest, Checkin checkin, String desiredBuild, String locale, Long loggingId, String marketCheckin, List macAddress, String meid, List accountCookie, String timeZone, Long securityToken, Integer version, List otaCert, String serial, String esn, DeviceConfig deviceConfiguration, List macAddressType, Integer fragment, String userName, Integer userSerialNumber) { 162 | this.imei = imei; 163 | this.androidId = androidId; 164 | this.digest = digest; 165 | this.checkin = checkin; 166 | this.desiredBuild = desiredBuild; 167 | this.locale = locale; 168 | this.loggingId = loggingId; 169 | this.marketCheckin = marketCheckin; 170 | this.macAddress = immutableCopyOf(macAddress); 171 | this.meid = meid; 172 | this.accountCookie = immutableCopyOf(accountCookie); 173 | this.timeZone = timeZone; 174 | this.securityToken = securityToken; 175 | this.version = version; 176 | this.otaCert = immutableCopyOf(otaCert); 177 | this.serial = serial; 178 | this.esn = esn; 179 | this.deviceConfiguration = deviceConfiguration; 180 | this.macAddressType = immutableCopyOf(macAddressType); 181 | this.fragment = fragment; 182 | this.userName = userName; 183 | this.userSerialNumber = userSerialNumber; 184 | } 185 | 186 | private Request(Builder builder) { 187 | this(builder.imei, builder.androidId, builder.digest, builder.checkin, builder.desiredBuild, builder.locale, builder.loggingId, builder.marketCheckin, builder.macAddress, builder.meid, builder.accountCookie, builder.timeZone, builder.securityToken, builder.version, builder.otaCert, builder.serial, builder.esn, builder.deviceConfiguration, builder.macAddressType, builder.fragment, builder.userName, builder.userSerialNumber); 188 | setBuilder(builder); 189 | } 190 | 191 | @Override 192 | public boolean equals(Object other) { 193 | if (other == this) return true; 194 | if (!(other instanceof Request)) return false; 195 | Request o = (Request) other; 196 | return equals(imei, o.imei) 197 | && equals(androidId, o.androidId) 198 | && equals(digest, o.digest) 199 | && equals(checkin, o.checkin) 200 | && equals(desiredBuild, o.desiredBuild) 201 | && equals(locale, o.locale) 202 | && equals(loggingId, o.loggingId) 203 | && equals(marketCheckin, o.marketCheckin) 204 | && equals(macAddress, o.macAddress) 205 | && equals(meid, o.meid) 206 | && equals(accountCookie, o.accountCookie) 207 | && equals(timeZone, o.timeZone) 208 | && equals(securityToken, o.securityToken) 209 | && equals(version, o.version) 210 | && equals(otaCert, o.otaCert) 211 | && equals(serial, o.serial) 212 | && equals(esn, o.esn) 213 | && equals(deviceConfiguration, o.deviceConfiguration) 214 | && equals(macAddressType, o.macAddressType) 215 | && equals(fragment, o.fragment) 216 | && equals(userName, o.userName) 217 | && equals(userSerialNumber, o.userSerialNumber); 218 | } 219 | 220 | @Override 221 | public int hashCode() { 222 | int result = hashCode; 223 | if (result == 0) { 224 | result = imei != null ? imei.hashCode() : 0; 225 | result = result * 37 + (androidId != null ? androidId.hashCode() : 0); 226 | result = result * 37 + (digest != null ? digest.hashCode() : 0); 227 | result = result * 37 + (checkin != null ? checkin.hashCode() : 0); 228 | result = result * 37 + (desiredBuild != null ? desiredBuild.hashCode() : 0); 229 | result = result * 37 + (locale != null ? locale.hashCode() : 0); 230 | result = result * 37 + (loggingId != null ? loggingId.hashCode() : 0); 231 | result = result * 37 + (marketCheckin != null ? marketCheckin.hashCode() : 0); 232 | result = result * 37 + (macAddress != null ? macAddress.hashCode() : 1); 233 | result = result * 37 + (meid != null ? meid.hashCode() : 0); 234 | result = result * 37 + (accountCookie != null ? accountCookie.hashCode() : 1); 235 | result = result * 37 + (timeZone != null ? timeZone.hashCode() : 0); 236 | result = result * 37 + (securityToken != null ? securityToken.hashCode() : 0); 237 | result = result * 37 + (version != null ? version.hashCode() : 0); 238 | result = result * 37 + (otaCert != null ? otaCert.hashCode() : 1); 239 | result = result * 37 + (serial != null ? serial.hashCode() : 0); 240 | result = result * 37 + (esn != null ? esn.hashCode() : 0); 241 | result = result * 37 + (deviceConfiguration != null ? deviceConfiguration.hashCode() : 0); 242 | result = result * 37 + (macAddressType != null ? macAddressType.hashCode() : 1); 243 | result = result * 37 + (fragment != null ? fragment.hashCode() : 0); 244 | result = result * 37 + (userName != null ? userName.hashCode() : 0); 245 | result = result * 37 + (userSerialNumber != null ? userSerialNumber.hashCode() : 0); 246 | hashCode = result; 247 | } 248 | return result; 249 | } 250 | 251 | public static final class Builder extends Message.Builder { 252 | 253 | public String imei; 254 | public Long androidId; 255 | public String digest; 256 | public Checkin checkin; 257 | public String desiredBuild; 258 | public String locale; 259 | public Long loggingId; 260 | public String marketCheckin; 261 | public List macAddress; 262 | public String meid; 263 | public List accountCookie; 264 | public String timeZone; 265 | public Long securityToken; 266 | public Integer version; 267 | public List otaCert; 268 | public String serial; 269 | public String esn; 270 | public DeviceConfig deviceConfiguration; 271 | public List macAddressType; 272 | public Integer fragment; 273 | public String userName; 274 | public Integer userSerialNumber; 275 | 276 | public Builder() { 277 | } 278 | 279 | public Builder(Request message) { 280 | super(message); 281 | if (message == null) return; 282 | this.imei = message.imei; 283 | this.androidId = message.androidId; 284 | this.digest = message.digest; 285 | this.checkin = message.checkin; 286 | this.desiredBuild = message.desiredBuild; 287 | this.locale = message.locale; 288 | this.loggingId = message.loggingId; 289 | this.marketCheckin = message.marketCheckin; 290 | this.macAddress = copyOf(message.macAddress); 291 | this.meid = message.meid; 292 | this.accountCookie = copyOf(message.accountCookie); 293 | this.timeZone = message.timeZone; 294 | this.securityToken = message.securityToken; 295 | this.version = message.version; 296 | this.otaCert = copyOf(message.otaCert); 297 | this.serial = message.serial; 298 | this.esn = message.esn; 299 | this.deviceConfiguration = message.deviceConfiguration; 300 | this.macAddressType = copyOf(message.macAddressType); 301 | this.fragment = message.fragment; 302 | this.userName = message.userName; 303 | this.userSerialNumber = message.userSerialNumber; 304 | } 305 | 306 | public Builder imei(String imei) { 307 | this.imei = imei; 308 | return this; 309 | } 310 | 311 | /** 312 | * unused 313 | */ 314 | public Builder androidId(Long androidId) { 315 | this.androidId = androidId; 316 | return this; 317 | } 318 | 319 | /** 320 | * Gservices["android_id"] or 0 321 | */ 322 | public Builder digest(String digest) { 323 | this.digest = digest; 324 | return this; 325 | } 326 | 327 | /** 328 | * Gservices["digest"] or "" 329 | */ 330 | public Builder checkin(Checkin checkin) { 331 | this.checkin = checkin; 332 | return this; 333 | } 334 | 335 | public Builder desiredBuild(String desiredBuild) { 336 | this.desiredBuild = desiredBuild; 337 | return this; 338 | } 339 | 340 | /** 341 | * unused 342 | */ 343 | public Builder locale(String locale) { 344 | this.locale = locale; 345 | return this; 346 | } 347 | 348 | /** 349 | * Locale.toString 350 | */ 351 | public Builder loggingId(Long loggingId) { 352 | this.loggingId = loggingId; 353 | return this; 354 | } 355 | 356 | /** 357 | * GoogleSettingsContract.Partner["logging_id2"] 358 | */ 359 | public Builder marketCheckin(String marketCheckin) { 360 | this.marketCheckin = marketCheckin; 361 | return this; 362 | } 363 | 364 | /** 365 | * unused 366 | */ 367 | public Builder macAddress(List macAddress) { 368 | this.macAddress = checkForNulls(macAddress); 369 | return this; 370 | } 371 | 372 | /** 373 | * NetworkInfo.getExtraInfo, WifiInfo.getMacAddress (12 hex-digits) 374 | */ 375 | public Builder meid(String meid) { 376 | this.meid = meid; 377 | return this; 378 | } 379 | 380 | /** 381 | * TelephonyManager.getDeviceId (14 hex-digits) 382 | */ 383 | public Builder accountCookie(List accountCookie) { 384 | this.accountCookie = checkForNulls(accountCookie); 385 | return this; 386 | } 387 | 388 | /** 389 | * "[]" followed by "" 390 | */ 391 | public Builder timeZone(String timeZone) { 392 | this.timeZone = timeZone; 393 | return this; 394 | } 395 | 396 | /** 397 | * TimeZone.getId 398 | */ 399 | public Builder securityToken(Long securityToken) { 400 | this.securityToken = securityToken; 401 | return this; 402 | } 403 | 404 | public Builder version(Integer version) { 405 | this.version = version; 406 | return this; 407 | } 408 | 409 | /** 410 | * 3 if securityToken != 0 OR androidId == 0 411 | */ 412 | public Builder otaCert(List otaCert) { 413 | this.otaCert = checkForNulls(otaCert); 414 | return this; 415 | } 416 | 417 | /** 418 | * SHA-1 of each in /system/etc/security/otacerts.zip or "--IOException--" or "--no-output--" 419 | */ 420 | public Builder serial(String serial) { 421 | this.serial = serial; 422 | return this; 423 | } 424 | 425 | /** 426 | * Build.SERIAL != "unknown" 427 | */ 428 | public Builder esn(String esn) { 429 | this.esn = esn; 430 | return this; 431 | } 432 | 433 | /** 434 | * TelephonyManager.getDeviceId (8 hex-digits) 435 | */ 436 | public Builder deviceConfiguration(DeviceConfig deviceConfiguration) { 437 | this.deviceConfiguration = deviceConfiguration; 438 | return this; 439 | } 440 | 441 | public Builder macAddressType(List macAddressType) { 442 | this.macAddressType = checkForNulls(macAddressType); 443 | return this; 444 | } 445 | 446 | /** 447 | * "ethernet", "wifi" 448 | */ 449 | public Builder fragment(Integer fragment) { 450 | this.fragment = fragment; 451 | return this; 452 | } 453 | 454 | /** 455 | * unknown (use 0) 456 | */ 457 | public Builder userName(String userName) { 458 | this.userName = userName; 459 | return this; 460 | } 461 | 462 | /** 463 | * unused 464 | */ 465 | public Builder userSerialNumber(Integer userSerialNumber) { 466 | this.userSerialNumber = userSerialNumber; 467 | return this; 468 | } 469 | 470 | @Override 471 | public Request build() { 472 | return new Request(this); 473 | } 474 | } 475 | 476 | public static final class DeviceConfig extends Message { 477 | 478 | public static final Integer DEFAULT_TOUCHSCREEN = 0; 479 | public static final Integer DEFAULT_KEYBOARDTYPE = 0; 480 | public static final Integer DEFAULT_NAVIGATION = 0; 481 | public static final Integer DEFAULT_SCREENLAYOUT = 0; 482 | public static final Boolean DEFAULT_HASHARDKEYBOARD = false; 483 | public static final Boolean DEFAULT_HASFIVEWAYNAVIGATION = false; 484 | public static final Integer DEFAULT_DENSITYDPI = 0; 485 | public static final Integer DEFAULT_GLESVERSION = 0; 486 | public static final List DEFAULT_SHAREDLIBRARY = Collections.emptyList(); 487 | public static final List DEFAULT_AVAILABLEFEATURE = Collections.emptyList(); 488 | public static final List DEFAULT_NATIVEPLATFORM = Collections.emptyList(); 489 | public static final Integer DEFAULT_WIDTHPIXELS = 0; 490 | public static final Integer DEFAULT_HEIGHTPIXELS = 0; 491 | public static final List DEFAULT_LOCALE = Collections.emptyList(); 492 | public static final List DEFAULT_GLEXTENSION = Collections.emptyList(); 493 | public static final Integer DEFAULT_DEVICECLASS = 0; 494 | public static final Integer DEFAULT_MAXAPKDOWNLOADSIZEMB = 0; 495 | 496 | @ProtoField(tag = 1, type = INT32) 497 | public final Integer touchScreen; 498 | 499 | /** 500 | * ConfigurationInfo.reqTouchScreen 501 | */ 502 | @ProtoField(tag = 2, type = INT32) 503 | public final Integer keyboardType; 504 | 505 | /** 506 | * ConfigurationInfo.reqKeyboardType 507 | */ 508 | @ProtoField(tag = 3, type = INT32) 509 | public final Integer navigation; 510 | 511 | /** 512 | * ConfigurationInfo.reqNavigation 513 | */ 514 | @ProtoField(tag = 4, type = INT32) 515 | public final Integer screenLayout; 516 | 517 | /** 518 | * ConfigurationInfo.screenLayout 519 | */ 520 | @ProtoField(tag = 5, type = BOOL) 521 | public final Boolean hasHardKeyboard; 522 | 523 | /** 524 | * ConfigurationInfo.reqInputFeatures & ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD 525 | */ 526 | @ProtoField(tag = 6, type = BOOL) 527 | public final Boolean hasFiveWayNavigation; 528 | 529 | /** 530 | * ConfigurationInfo.reqInputFeatures & ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV 531 | */ 532 | @ProtoField(tag = 7, type = INT32) 533 | public final Integer densityDpi; 534 | 535 | /** 536 | * DisplayMetrics.densityDpi 537 | */ 538 | @ProtoField(tag = 8, type = INT32) 539 | public final Integer glEsVersion; 540 | 541 | /** 542 | * ConfigurationInfo.reqGlEsVersion 543 | */ 544 | @ProtoField(tag = 9, type = STRING, label = REPEATED) 545 | public final List sharedLibrary; 546 | 547 | /** 548 | * PackageManager.getSystemSharedLibraryNames 549 | */ 550 | @ProtoField(tag = 10, type = STRING, label = REPEATED) 551 | public final List availableFeature; 552 | 553 | /** 554 | * PackageManager.getSystemAvailableFeatures 555 | */ 556 | @ProtoField(tag = 11, type = STRING, label = REPEATED) 557 | public final List nativePlatform; 558 | 559 | /** 560 | * Build.CPU_ABI (and Build.CPU_ABI2 != "unknown") 561 | */ 562 | @ProtoField(tag = 12, type = INT32) 563 | public final Integer widthPixels; 564 | 565 | /** 566 | * DisplayMetrics.widthPixels 567 | */ 568 | @ProtoField(tag = 13, type = INT32) 569 | public final Integer heightPixels; 570 | 571 | /** 572 | * DisplayMetrics.heightPixels 573 | */ 574 | @ProtoField(tag = 14, type = STRING, label = REPEATED) 575 | public final List locale; 576 | 577 | /** 578 | * Context.getAssets.getLocales 579 | */ 580 | @ProtoField(tag = 15, type = STRING, label = REPEATED) 581 | public final List glExtension; 582 | 583 | /** 584 | * GLES10.glGetString(GLES10.GL_EXTENSIONS) 585 | */ 586 | @ProtoField(tag = 16, type = INT32) 587 | public final Integer deviceClass; 588 | 589 | /** 590 | * unused 591 | */ 592 | @ProtoField(tag = 17, type = INT32) 593 | public final Integer maxApkDownloadSizeMb; 594 | 595 | public DeviceConfig(Integer touchScreen, Integer keyboardType, Integer navigation, Integer screenLayout, Boolean hasHardKeyboard, Boolean hasFiveWayNavigation, Integer densityDpi, Integer glEsVersion, List sharedLibrary, List availableFeature, List nativePlatform, Integer widthPixels, Integer heightPixels, List locale, List glExtension, Integer deviceClass, Integer maxApkDownloadSizeMb) { 596 | this.touchScreen = touchScreen; 597 | this.keyboardType = keyboardType; 598 | this.navigation = navigation; 599 | this.screenLayout = screenLayout; 600 | this.hasHardKeyboard = hasHardKeyboard; 601 | this.hasFiveWayNavigation = hasFiveWayNavigation; 602 | this.densityDpi = densityDpi; 603 | this.glEsVersion = glEsVersion; 604 | this.sharedLibrary = immutableCopyOf(sharedLibrary); 605 | this.availableFeature = immutableCopyOf(availableFeature); 606 | this.nativePlatform = immutableCopyOf(nativePlatform); 607 | this.widthPixels = widthPixels; 608 | this.heightPixels = heightPixels; 609 | this.locale = immutableCopyOf(locale); 610 | this.glExtension = immutableCopyOf(glExtension); 611 | this.deviceClass = deviceClass; 612 | this.maxApkDownloadSizeMb = maxApkDownloadSizeMb; 613 | } 614 | 615 | private DeviceConfig(Builder builder) { 616 | this(builder.touchScreen, builder.keyboardType, builder.navigation, builder.screenLayout, builder.hasHardKeyboard, builder.hasFiveWayNavigation, builder.densityDpi, builder.glEsVersion, builder.sharedLibrary, builder.availableFeature, builder.nativePlatform, builder.widthPixels, builder.heightPixels, builder.locale, builder.glExtension, builder.deviceClass, builder.maxApkDownloadSizeMb); 617 | setBuilder(builder); 618 | } 619 | 620 | @Override 621 | public boolean equals(Object other) { 622 | if (other == this) return true; 623 | if (!(other instanceof DeviceConfig)) return false; 624 | DeviceConfig o = (DeviceConfig) other; 625 | return equals(touchScreen, o.touchScreen) 626 | && equals(keyboardType, o.keyboardType) 627 | && equals(navigation, o.navigation) 628 | && equals(screenLayout, o.screenLayout) 629 | && equals(hasHardKeyboard, o.hasHardKeyboard) 630 | && equals(hasFiveWayNavigation, o.hasFiveWayNavigation) 631 | && equals(densityDpi, o.densityDpi) 632 | && equals(glEsVersion, o.glEsVersion) 633 | && equals(sharedLibrary, o.sharedLibrary) 634 | && equals(availableFeature, o.availableFeature) 635 | && equals(nativePlatform, o.nativePlatform) 636 | && equals(widthPixels, o.widthPixels) 637 | && equals(heightPixels, o.heightPixels) 638 | && equals(locale, o.locale) 639 | && equals(glExtension, o.glExtension) 640 | && equals(deviceClass, o.deviceClass) 641 | && equals(maxApkDownloadSizeMb, o.maxApkDownloadSizeMb); 642 | } 643 | 644 | @Override 645 | public int hashCode() { 646 | int result = hashCode; 647 | if (result == 0) { 648 | result = touchScreen != null ? touchScreen.hashCode() : 0; 649 | result = result * 37 + (keyboardType != null ? keyboardType.hashCode() : 0); 650 | result = result * 37 + (navigation != null ? navigation.hashCode() : 0); 651 | result = result * 37 + (screenLayout != null ? screenLayout.hashCode() : 0); 652 | result = result * 37 + (hasHardKeyboard != null ? hasHardKeyboard.hashCode() : 0); 653 | result = result * 37 + (hasFiveWayNavigation != null ? hasFiveWayNavigation.hashCode() : 0); 654 | result = result * 37 + (densityDpi != null ? densityDpi.hashCode() : 0); 655 | result = result * 37 + (glEsVersion != null ? glEsVersion.hashCode() : 0); 656 | result = result * 37 + (sharedLibrary != null ? sharedLibrary.hashCode() : 1); 657 | result = result * 37 + (availableFeature != null ? availableFeature.hashCode() : 1); 658 | result = result * 37 + (nativePlatform != null ? nativePlatform.hashCode() : 1); 659 | result = result * 37 + (widthPixels != null ? widthPixels.hashCode() : 0); 660 | result = result * 37 + (heightPixels != null ? heightPixels.hashCode() : 0); 661 | result = result * 37 + (locale != null ? locale.hashCode() : 1); 662 | result = result * 37 + (glExtension != null ? glExtension.hashCode() : 1); 663 | result = result * 37 + (deviceClass != null ? deviceClass.hashCode() : 0); 664 | result = result * 37 + (maxApkDownloadSizeMb != null ? maxApkDownloadSizeMb.hashCode() : 0); 665 | hashCode = result; 666 | } 667 | return result; 668 | } 669 | 670 | public static final class Builder extends Message.Builder { 671 | 672 | public Integer touchScreen; 673 | public Integer keyboardType; 674 | public Integer navigation; 675 | public Integer screenLayout; 676 | public Boolean hasHardKeyboard; 677 | public Boolean hasFiveWayNavigation; 678 | public Integer densityDpi; 679 | public Integer glEsVersion; 680 | public List sharedLibrary; 681 | public List availableFeature; 682 | public List nativePlatform; 683 | public Integer widthPixels; 684 | public Integer heightPixels; 685 | public List locale; 686 | public List glExtension; 687 | public Integer deviceClass; 688 | public Integer maxApkDownloadSizeMb; 689 | 690 | public Builder() { 691 | } 692 | 693 | public Builder(DeviceConfig message) { 694 | super(message); 695 | if (message == null) return; 696 | this.touchScreen = message.touchScreen; 697 | this.keyboardType = message.keyboardType; 698 | this.navigation = message.navigation; 699 | this.screenLayout = message.screenLayout; 700 | this.hasHardKeyboard = message.hasHardKeyboard; 701 | this.hasFiveWayNavigation = message.hasFiveWayNavigation; 702 | this.densityDpi = message.densityDpi; 703 | this.glEsVersion = message.glEsVersion; 704 | this.sharedLibrary = copyOf(message.sharedLibrary); 705 | this.availableFeature = copyOf(message.availableFeature); 706 | this.nativePlatform = copyOf(message.nativePlatform); 707 | this.widthPixels = message.widthPixels; 708 | this.heightPixels = message.heightPixels; 709 | this.locale = copyOf(message.locale); 710 | this.glExtension = copyOf(message.glExtension); 711 | this.deviceClass = message.deviceClass; 712 | this.maxApkDownloadSizeMb = message.maxApkDownloadSizeMb; 713 | } 714 | 715 | public Builder touchScreen(Integer touchScreen) { 716 | this.touchScreen = touchScreen; 717 | return this; 718 | } 719 | 720 | /** 721 | * ConfigurationInfo.reqTouchScreen 722 | */ 723 | public Builder keyboardType(Integer keyboardType) { 724 | this.keyboardType = keyboardType; 725 | return this; 726 | } 727 | 728 | /** 729 | * ConfigurationInfo.reqKeyboardType 730 | */ 731 | public Builder navigation(Integer navigation) { 732 | this.navigation = navigation; 733 | return this; 734 | } 735 | 736 | /** 737 | * ConfigurationInfo.reqNavigation 738 | */ 739 | public Builder screenLayout(Integer screenLayout) { 740 | this.screenLayout = screenLayout; 741 | return this; 742 | } 743 | 744 | /** 745 | * ConfigurationInfo.screenLayout 746 | */ 747 | public Builder hasHardKeyboard(Boolean hasHardKeyboard) { 748 | this.hasHardKeyboard = hasHardKeyboard; 749 | return this; 750 | } 751 | 752 | /** 753 | * ConfigurationInfo.reqInputFeatures & ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD 754 | */ 755 | public Builder hasFiveWayNavigation(Boolean hasFiveWayNavigation) { 756 | this.hasFiveWayNavigation = hasFiveWayNavigation; 757 | return this; 758 | } 759 | 760 | /** 761 | * ConfigurationInfo.reqInputFeatures & ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV 762 | */ 763 | public Builder densityDpi(Integer densityDpi) { 764 | this.densityDpi = densityDpi; 765 | return this; 766 | } 767 | 768 | /** 769 | * DisplayMetrics.densityDpi 770 | */ 771 | public Builder glEsVersion(Integer glEsVersion) { 772 | this.glEsVersion = glEsVersion; 773 | return this; 774 | } 775 | 776 | /** 777 | * ConfigurationInfo.reqGlEsVersion 778 | */ 779 | public Builder sharedLibrary(List sharedLibrary) { 780 | this.sharedLibrary = checkForNulls(sharedLibrary); 781 | return this; 782 | } 783 | 784 | /** 785 | * PackageManager.getSystemSharedLibraryNames 786 | */ 787 | public Builder availableFeature(List availableFeature) { 788 | this.availableFeature = checkForNulls(availableFeature); 789 | return this; 790 | } 791 | 792 | /** 793 | * PackageManager.getSystemAvailableFeatures 794 | */ 795 | public Builder nativePlatform(List nativePlatform) { 796 | this.nativePlatform = checkForNulls(nativePlatform); 797 | return this; 798 | } 799 | 800 | /** 801 | * Build.CPU_ABI (and Build.CPU_ABI2 != "unknown") 802 | */ 803 | public Builder widthPixels(Integer widthPixels) { 804 | this.widthPixels = widthPixels; 805 | return this; 806 | } 807 | 808 | /** 809 | * DisplayMetrics.widthPixels 810 | */ 811 | public Builder heightPixels(Integer heightPixels) { 812 | this.heightPixels = heightPixels; 813 | return this; 814 | } 815 | 816 | /** 817 | * DisplayMetrics.heightPixels 818 | */ 819 | public Builder locale(List locale) { 820 | this.locale = checkForNulls(locale); 821 | return this; 822 | } 823 | 824 | /** 825 | * Context.getAssets.getLocales 826 | */ 827 | public Builder glExtension(List glExtension) { 828 | this.glExtension = checkForNulls(glExtension); 829 | return this; 830 | } 831 | 832 | /** 833 | * GLES10.glGetString(GLES10.GL_EXTENSIONS) 834 | */ 835 | public Builder deviceClass(Integer deviceClass) { 836 | this.deviceClass = deviceClass; 837 | return this; 838 | } 839 | 840 | /** 841 | * unused 842 | */ 843 | public Builder maxApkDownloadSizeMb(Integer maxApkDownloadSizeMb) { 844 | this.maxApkDownloadSizeMb = maxApkDownloadSizeMb; 845 | return this; 846 | } 847 | 848 | @Override 849 | public DeviceConfig build() { 850 | return new DeviceConfig(this); 851 | } 852 | } 853 | } 854 | 855 | public static final class Checkin extends Message { 856 | 857 | public static final Long DEFAULT_LASTCHECKINMS = 0L; 858 | public static final List DEFAULT_EVENT = Collections.emptyList(); 859 | public static final List DEFAULT_STAT = Collections.emptyList(); 860 | public static final List DEFAULT_REQUESTEDGROUP = Collections.emptyList(); 861 | public static final String DEFAULT_CELLOPERATOR = ""; 862 | public static final String DEFAULT_SIMOPERATOR = ""; 863 | public static final String DEFAULT_ROAMING = ""; 864 | public static final Integer DEFAULT_USERNUMBER = 0; 865 | 866 | @ProtoField(tag = 1) 867 | public final Checkin.Build build; 868 | 869 | @ProtoField(tag = 2, type = INT64) 870 | public final Long lastCheckinMs; 871 | 872 | /** 873 | * or 0 874 | */ 875 | @ProtoField(tag = 3, label = REPEATED) 876 | public final List event; 877 | 878 | @ProtoField(tag = 4, label = REPEATED) 879 | public final List stat; 880 | 881 | @ProtoField(tag = 5, type = STRING, label = REPEATED) 882 | public final List requestedGroup; 883 | 884 | /** 885 | * unused 886 | */ 887 | @ProtoField(tag = 6, type = STRING) 888 | public final String cellOperator; 889 | 890 | /** 891 | * TelephonyManager.getNetworkOperator != null|empty 892 | */ 893 | @ProtoField(tag = 7, type = STRING) 894 | public final String simOperator; 895 | 896 | /** 897 | * TelephonyManager.getSimOperator != null|empty 898 | */ 899 | @ProtoField(tag = 8, type = STRING) 900 | public final String roaming; 901 | 902 | /** 903 | * "mobile/notmobile/unknown-roaming/notroaming/unknown" 904 | */ 905 | @ProtoField(tag = 9, type = INT32) 906 | public final Integer userNumber; 907 | 908 | public Checkin(Checkin.Build build, Long lastCheckinMs, List event, List stat, List requestedGroup, String cellOperator, String simOperator, String roaming, Integer userNumber) { 909 | this.build = build; 910 | this.lastCheckinMs = lastCheckinMs; 911 | this.event = immutableCopyOf(event); 912 | this.stat = immutableCopyOf(stat); 913 | this.requestedGroup = immutableCopyOf(requestedGroup); 914 | this.cellOperator = cellOperator; 915 | this.simOperator = simOperator; 916 | this.roaming = roaming; 917 | this.userNumber = userNumber; 918 | } 919 | 920 | private Checkin(Builder builder) { 921 | this(builder.build, builder.lastCheckinMs, builder.event, builder.stat, builder.requestedGroup, builder.cellOperator, builder.simOperator, builder.roaming, builder.userNumber); 922 | setBuilder(builder); 923 | } 924 | 925 | @Override 926 | public boolean equals(Object other) { 927 | if (other == this) return true; 928 | if (!(other instanceof Checkin)) return false; 929 | Checkin o = (Checkin) other; 930 | return equals(build, o.build) 931 | && equals(lastCheckinMs, o.lastCheckinMs) 932 | && equals(event, o.event) 933 | && equals(stat, o.stat) 934 | && equals(requestedGroup, o.requestedGroup) 935 | && equals(cellOperator, o.cellOperator) 936 | && equals(simOperator, o.simOperator) 937 | && equals(roaming, o.roaming) 938 | && equals(userNumber, o.userNumber); 939 | } 940 | 941 | @Override 942 | public int hashCode() { 943 | int result = hashCode; 944 | if (result == 0) { 945 | result = build != null ? build.hashCode() : 0; 946 | result = result * 37 + (lastCheckinMs != null ? lastCheckinMs.hashCode() : 0); 947 | result = result * 37 + (event != null ? event.hashCode() : 1); 948 | result = result * 37 + (stat != null ? stat.hashCode() : 1); 949 | result = result * 37 + (requestedGroup != null ? requestedGroup.hashCode() : 1); 950 | result = result * 37 + (cellOperator != null ? cellOperator.hashCode() : 0); 951 | result = result * 37 + (simOperator != null ? simOperator.hashCode() : 0); 952 | result = result * 37 + (roaming != null ? roaming.hashCode() : 0); 953 | result = result * 37 + (userNumber != null ? userNumber.hashCode() : 0); 954 | hashCode = result; 955 | } 956 | return result; 957 | } 958 | 959 | public static final class Builder extends Message.Builder { 960 | 961 | public Checkin.Build build; 962 | public Long lastCheckinMs; 963 | public List event; 964 | public List stat; 965 | public List requestedGroup; 966 | public String cellOperator; 967 | public String simOperator; 968 | public String roaming; 969 | public Integer userNumber; 970 | 971 | public Builder() { 972 | } 973 | 974 | public Builder(Checkin message) { 975 | super(message); 976 | if (message == null) return; 977 | this.build = message.build; 978 | this.lastCheckinMs = message.lastCheckinMs; 979 | this.event = copyOf(message.event); 980 | this.stat = copyOf(message.stat); 981 | this.requestedGroup = copyOf(message.requestedGroup); 982 | this.cellOperator = message.cellOperator; 983 | this.simOperator = message.simOperator; 984 | this.roaming = message.roaming; 985 | this.userNumber = message.userNumber; 986 | } 987 | 988 | public Builder build(Checkin.Build build) { 989 | this.build = build; 990 | return this; 991 | } 992 | 993 | public Builder lastCheckinMs(Long lastCheckinMs) { 994 | this.lastCheckinMs = lastCheckinMs; 995 | return this; 996 | } 997 | 998 | /** 999 | * or 0 1000 | */ 1001 | public Builder event(List event) { 1002 | this.event = checkForNulls(event); 1003 | return this; 1004 | } 1005 | 1006 | public Builder stat(List stat) { 1007 | this.stat = checkForNulls(stat); 1008 | return this; 1009 | } 1010 | 1011 | public Builder requestedGroup(List requestedGroup) { 1012 | this.requestedGroup = checkForNulls(requestedGroup); 1013 | return this; 1014 | } 1015 | 1016 | /** 1017 | * unused 1018 | */ 1019 | public Builder cellOperator(String cellOperator) { 1020 | this.cellOperator = cellOperator; 1021 | return this; 1022 | } 1023 | 1024 | /** 1025 | * TelephonyManager.getNetworkOperator != null|empty 1026 | */ 1027 | public Builder simOperator(String simOperator) { 1028 | this.simOperator = simOperator; 1029 | return this; 1030 | } 1031 | 1032 | /** 1033 | * TelephonyManager.getSimOperator != null|empty 1034 | */ 1035 | public Builder roaming(String roaming) { 1036 | this.roaming = roaming; 1037 | return this; 1038 | } 1039 | 1040 | /** 1041 | * "mobile/notmobile/unknown-roaming/notroaming/unknown" 1042 | */ 1043 | public Builder userNumber(Integer userNumber) { 1044 | this.userNumber = userNumber; 1045 | return this; 1046 | } 1047 | 1048 | @Override 1049 | public Checkin build() { 1050 | return new Checkin(this); 1051 | } 1052 | } 1053 | 1054 | public static final class Event extends Message { 1055 | 1056 | public static final String DEFAULT_TAG = ""; 1057 | public static final String DEFAULT_VALUE = ""; 1058 | public static final Long DEFAULT_TIMEMS = 0L; 1059 | 1060 | @ProtoField(tag = 1, type = STRING) 1061 | public final String tag; 1062 | 1063 | @ProtoField(tag = 2, type = STRING) 1064 | public final String value; 1065 | 1066 | @ProtoField(tag = 3, type = INT64) 1067 | public final Long timeMs; 1068 | 1069 | public Event(String tag, String value, Long timeMs) { 1070 | this.tag = tag; 1071 | this.value = value; 1072 | this.timeMs = timeMs; 1073 | } 1074 | 1075 | private Event(Builder builder) { 1076 | this(builder.tag, builder.value, builder.timeMs); 1077 | setBuilder(builder); 1078 | } 1079 | 1080 | @Override 1081 | public boolean equals(Object other) { 1082 | if (other == this) return true; 1083 | if (!(other instanceof Event)) return false; 1084 | Event o = (Event) other; 1085 | return equals(tag, o.tag) 1086 | && equals(value, o.value) 1087 | && equals(timeMs, o.timeMs); 1088 | } 1089 | 1090 | @Override 1091 | public int hashCode() { 1092 | int result = hashCode; 1093 | if (result == 0) { 1094 | result = tag != null ? tag.hashCode() : 0; 1095 | result = result * 37 + (value != null ? value.hashCode() : 0); 1096 | result = result * 37 + (timeMs != null ? timeMs.hashCode() : 0); 1097 | hashCode = result; 1098 | } 1099 | return result; 1100 | } 1101 | 1102 | public static final class Builder extends Message.Builder { 1103 | 1104 | public String tag; 1105 | public String value; 1106 | public Long timeMs; 1107 | 1108 | public Builder() { 1109 | } 1110 | 1111 | public Builder(Event message) { 1112 | super(message); 1113 | if (message == null) return; 1114 | this.tag = message.tag; 1115 | this.value = message.value; 1116 | this.timeMs = message.timeMs; 1117 | } 1118 | 1119 | public Builder tag(String tag) { 1120 | this.tag = tag; 1121 | return this; 1122 | } 1123 | 1124 | public Builder value(String value) { 1125 | this.value = value; 1126 | return this; 1127 | } 1128 | 1129 | public Builder timeMs(Long timeMs) { 1130 | this.timeMs = timeMs; 1131 | return this; 1132 | } 1133 | 1134 | @Override 1135 | public Event build() { 1136 | return new Event(this); 1137 | } 1138 | } 1139 | } 1140 | 1141 | public static final class Statistic extends Message { 1142 | 1143 | public static final String DEFAULT_TAG = ""; 1144 | public static final Integer DEFAULT_COUNT = 0; 1145 | public static final Float DEFAULT_SUM = 0F; 1146 | 1147 | @ProtoField(tag = 1, type = STRING) 1148 | public final String tag; 1149 | 1150 | @ProtoField(tag = 2, type = INT32) 1151 | public final Integer count; 1152 | 1153 | @ProtoField(tag = 3, type = FLOAT) 1154 | public final Float sum; 1155 | 1156 | public Statistic(String tag, Integer count, Float sum) { 1157 | this.tag = tag; 1158 | this.count = count; 1159 | this.sum = sum; 1160 | } 1161 | 1162 | private Statistic(Builder builder) { 1163 | this(builder.tag, builder.count, builder.sum); 1164 | setBuilder(builder); 1165 | } 1166 | 1167 | @Override 1168 | public boolean equals(Object other) { 1169 | if (other == this) return true; 1170 | if (!(other instanceof Statistic)) return false; 1171 | Statistic o = (Statistic) other; 1172 | return equals(tag, o.tag) 1173 | && equals(count, o.count) 1174 | && equals(sum, o.sum); 1175 | } 1176 | 1177 | @Override 1178 | public int hashCode() { 1179 | int result = hashCode; 1180 | if (result == 0) { 1181 | result = tag != null ? tag.hashCode() : 0; 1182 | result = result * 37 + (count != null ? count.hashCode() : 0); 1183 | result = result * 37 + (sum != null ? sum.hashCode() : 0); 1184 | hashCode = result; 1185 | } 1186 | return result; 1187 | } 1188 | 1189 | public static final class Builder extends Message.Builder { 1190 | 1191 | public String tag; 1192 | public Integer count; 1193 | public Float sum; 1194 | 1195 | public Builder() { 1196 | } 1197 | 1198 | public Builder(Statistic message) { 1199 | super(message); 1200 | if (message == null) return; 1201 | this.tag = message.tag; 1202 | this.count = message.count; 1203 | this.sum = message.sum; 1204 | } 1205 | 1206 | public Builder tag(String tag) { 1207 | this.tag = tag; 1208 | return this; 1209 | } 1210 | 1211 | public Builder count(Integer count) { 1212 | this.count = count; 1213 | return this; 1214 | } 1215 | 1216 | public Builder sum(Float sum) { 1217 | this.sum = sum; 1218 | return this; 1219 | } 1220 | 1221 | @Override 1222 | public Statistic build() { 1223 | return new Statistic(this); 1224 | } 1225 | } 1226 | } 1227 | 1228 | public static final class Build extends Message { 1229 | 1230 | public static final String DEFAULT_FINGERPRINT = ""; 1231 | public static final String DEFAULT_HARDWARE = ""; 1232 | public static final String DEFAULT_BRAND = ""; 1233 | public static final String DEFAULT_RADIO = ""; 1234 | public static final String DEFAULT_BOOTLOADER = ""; 1235 | public static final String DEFAULT_CLIENTID = ""; 1236 | public static final Long DEFAULT_TIME = 0L; 1237 | public static final Integer DEFAULT_PACKAGEVERSIONCODE = 0; 1238 | public static final String DEFAULT_DEVICE = ""; 1239 | public static final Integer DEFAULT_SDKVERSION = 0; 1240 | public static final String DEFAULT_MODEL = ""; 1241 | public static final String DEFAULT_MANUFACTURER = ""; 1242 | public static final String DEFAULT_PRODUCT = ""; 1243 | public static final Boolean DEFAULT_OTAINSTALLED = false; 1244 | 1245 | @ProtoField(tag = 1, type = STRING) 1246 | public final String fingerprint; 1247 | 1248 | /** 1249 | * Build.FINGERPRINT 1250 | */ 1251 | @ProtoField(tag = 2, type = STRING) 1252 | public final String hardware; 1253 | 1254 | /** 1255 | * Build.HARDWARE 1256 | */ 1257 | @ProtoField(tag = 3, type = STRING) 1258 | public final String brand; 1259 | 1260 | /** 1261 | * Build.BRAND 1262 | */ 1263 | @ProtoField(tag = 4, type = STRING) 1264 | public final String radio; 1265 | 1266 | /** 1267 | * Build.getRadioVersion() 1268 | */ 1269 | @ProtoField(tag = 5, type = STRING) 1270 | public final String bootloader; 1271 | 1272 | /** 1273 | * Build.BOOTLOADER 1274 | */ 1275 | @ProtoField(tag = 6, type = STRING) 1276 | public final String clientId; 1277 | 1278 | /** 1279 | * GoogleSettingsContract.Partner["client_id"] 1280 | */ 1281 | @ProtoField(tag = 7, type = INT64) 1282 | public final Long time; 1283 | 1284 | /** 1285 | * Build.TIME / 1000L 1286 | */ 1287 | @ProtoField(tag = 8, type = INT32) 1288 | public final Integer packageVersionCode; 1289 | 1290 | /** 1291 | * PackageInfo.versionCode 1292 | */ 1293 | @ProtoField(tag = 9, type = STRING) 1294 | public final String device; 1295 | 1296 | /** 1297 | * Build.DEVICE 1298 | */ 1299 | @ProtoField(tag = 10, type = INT32) 1300 | public final Integer sdkVersion; 1301 | 1302 | /** 1303 | * Build.VERSION.SDK_INT 1304 | */ 1305 | @ProtoField(tag = 11, type = STRING) 1306 | public final String model; 1307 | 1308 | /** 1309 | * Build.MODEL 1310 | */ 1311 | @ProtoField(tag = 12, type = STRING) 1312 | public final String manufacturer; 1313 | 1314 | /** 1315 | * Build.MANUFACTURER 1316 | */ 1317 | @ProtoField(tag = 13, type = STRING) 1318 | public final String product; 1319 | 1320 | /** 1321 | * Build.PRODUCT 1322 | */ 1323 | @ProtoField(tag = 14, type = BOOL) 1324 | public final Boolean otaInstalled; 1325 | 1326 | public Build(String fingerprint, String hardware, String brand, String radio, String bootloader, String clientId, Long time, Integer packageVersionCode, String device, Integer sdkVersion, String model, String manufacturer, String product, Boolean otaInstalled) { 1327 | this.fingerprint = fingerprint; 1328 | this.hardware = hardware; 1329 | this.brand = brand; 1330 | this.radio = radio; 1331 | this.bootloader = bootloader; 1332 | this.clientId = clientId; 1333 | this.time = time; 1334 | this.packageVersionCode = packageVersionCode; 1335 | this.device = device; 1336 | this.sdkVersion = sdkVersion; 1337 | this.model = model; 1338 | this.manufacturer = manufacturer; 1339 | this.product = product; 1340 | this.otaInstalled = otaInstalled; 1341 | } 1342 | 1343 | private Build(Builder builder) { 1344 | this(builder.fingerprint, builder.hardware, builder.brand, builder.radio, builder.bootloader, builder.clientId, builder.time, builder.packageVersionCode, builder.device, builder.sdkVersion, builder.model, builder.manufacturer, builder.product, builder.otaInstalled); 1345 | setBuilder(builder); 1346 | } 1347 | 1348 | @Override 1349 | public boolean equals(Object other) { 1350 | if (other == this) return true; 1351 | if (!(other instanceof Build)) return false; 1352 | Build o = (Build) other; 1353 | return equals(fingerprint, o.fingerprint) 1354 | && equals(hardware, o.hardware) 1355 | && equals(brand, o.brand) 1356 | && equals(radio, o.radio) 1357 | && equals(bootloader, o.bootloader) 1358 | && equals(clientId, o.clientId) 1359 | && equals(time, o.time) 1360 | && equals(packageVersionCode, o.packageVersionCode) 1361 | && equals(device, o.device) 1362 | && equals(sdkVersion, o.sdkVersion) 1363 | && equals(model, o.model) 1364 | && equals(manufacturer, o.manufacturer) 1365 | && equals(product, o.product) 1366 | && equals(otaInstalled, o.otaInstalled); 1367 | } 1368 | 1369 | @Override 1370 | public int hashCode() { 1371 | int result = hashCode; 1372 | if (result == 0) { 1373 | result = fingerprint != null ? fingerprint.hashCode() : 0; 1374 | result = result * 37 + (hardware != null ? hardware.hashCode() : 0); 1375 | result = result * 37 + (brand != null ? brand.hashCode() : 0); 1376 | result = result * 37 + (radio != null ? radio.hashCode() : 0); 1377 | result = result * 37 + (bootloader != null ? bootloader.hashCode() : 0); 1378 | result = result * 37 + (clientId != null ? clientId.hashCode() : 0); 1379 | result = result * 37 + (time != null ? time.hashCode() : 0); 1380 | result = result * 37 + (packageVersionCode != null ? packageVersionCode.hashCode() : 0); 1381 | result = result * 37 + (device != null ? device.hashCode() : 0); 1382 | result = result * 37 + (sdkVersion != null ? sdkVersion.hashCode() : 0); 1383 | result = result * 37 + (model != null ? model.hashCode() : 0); 1384 | result = result * 37 + (manufacturer != null ? manufacturer.hashCode() : 0); 1385 | result = result * 37 + (product != null ? product.hashCode() : 0); 1386 | result = result * 37 + (otaInstalled != null ? otaInstalled.hashCode() : 0); 1387 | hashCode = result; 1388 | } 1389 | return result; 1390 | } 1391 | 1392 | public static final class Builder extends Message.Builder { 1393 | 1394 | public String fingerprint; 1395 | public String hardware; 1396 | public String brand; 1397 | public String radio; 1398 | public String bootloader; 1399 | public String clientId; 1400 | public Long time; 1401 | public Integer packageVersionCode; 1402 | public String device; 1403 | public Integer sdkVersion; 1404 | public String model; 1405 | public String manufacturer; 1406 | public String product; 1407 | public Boolean otaInstalled; 1408 | 1409 | public Builder() { 1410 | } 1411 | 1412 | public Builder(Build message) { 1413 | super(message); 1414 | if (message == null) return; 1415 | this.fingerprint = message.fingerprint; 1416 | this.hardware = message.hardware; 1417 | this.brand = message.brand; 1418 | this.radio = message.radio; 1419 | this.bootloader = message.bootloader; 1420 | this.clientId = message.clientId; 1421 | this.time = message.time; 1422 | this.packageVersionCode = message.packageVersionCode; 1423 | this.device = message.device; 1424 | this.sdkVersion = message.sdkVersion; 1425 | this.model = message.model; 1426 | this.manufacturer = message.manufacturer; 1427 | this.product = message.product; 1428 | this.otaInstalled = message.otaInstalled; 1429 | } 1430 | 1431 | public Builder fingerprint(String fingerprint) { 1432 | this.fingerprint = fingerprint; 1433 | return this; 1434 | } 1435 | 1436 | /** 1437 | * Build.FINGERPRINT 1438 | */ 1439 | public Builder hardware(String hardware) { 1440 | this.hardware = hardware; 1441 | return this; 1442 | } 1443 | 1444 | /** 1445 | * Build.HARDWARE 1446 | */ 1447 | public Builder brand(String brand) { 1448 | this.brand = brand; 1449 | return this; 1450 | } 1451 | 1452 | /** 1453 | * Build.BRAND 1454 | */ 1455 | public Builder radio(String radio) { 1456 | this.radio = radio; 1457 | return this; 1458 | } 1459 | 1460 | /** 1461 | * Build.getRadioVersion() 1462 | */ 1463 | public Builder bootloader(String bootloader) { 1464 | this.bootloader = bootloader; 1465 | return this; 1466 | } 1467 | 1468 | /** 1469 | * Build.BOOTLOADER 1470 | */ 1471 | public Builder clientId(String clientId) { 1472 | this.clientId = clientId; 1473 | return this; 1474 | } 1475 | 1476 | /** 1477 | * GoogleSettingsContract.Partner["client_id"] 1478 | */ 1479 | public Builder time(Long time) { 1480 | this.time = time; 1481 | return this; 1482 | } 1483 | 1484 | /** 1485 | * Build.TIME / 1000L 1486 | */ 1487 | public Builder packageVersionCode(Integer packageVersionCode) { 1488 | this.packageVersionCode = packageVersionCode; 1489 | return this; 1490 | } 1491 | 1492 | /** 1493 | * PackageInfo.versionCode 1494 | */ 1495 | public Builder device(String device) { 1496 | this.device = device; 1497 | return this; 1498 | } 1499 | 1500 | /** 1501 | * Build.DEVICE 1502 | */ 1503 | public Builder sdkVersion(Integer sdkVersion) { 1504 | this.sdkVersion = sdkVersion; 1505 | return this; 1506 | } 1507 | 1508 | /** 1509 | * Build.VERSION.SDK_INT 1510 | */ 1511 | public Builder model(String model) { 1512 | this.model = model; 1513 | return this; 1514 | } 1515 | 1516 | /** 1517 | * Build.MODEL 1518 | */ 1519 | public Builder manufacturer(String manufacturer) { 1520 | this.manufacturer = manufacturer; 1521 | return this; 1522 | } 1523 | 1524 | /** 1525 | * Build.MANUFACTURER 1526 | */ 1527 | public Builder product(String product) { 1528 | this.product = product; 1529 | return this; 1530 | } 1531 | 1532 | /** 1533 | * Build.PRODUCT 1534 | */ 1535 | public Builder otaInstalled(Boolean otaInstalled) { 1536 | this.otaInstalled = otaInstalled; 1537 | return this; 1538 | } 1539 | 1540 | @Override 1541 | public Build build() { 1542 | return new Build(this); 1543 | } 1544 | } 1545 | } 1546 | } 1547 | } 1548 | -------------------------------------------------------------------------------- /src/com/google/checkin/Response.java: -------------------------------------------------------------------------------- 1 | // Code generated by Wire protocol buffer compiler, do not edit. 2 | // Source file: protos-repo/CheckIn.proto 3 | package com.google.checkin; 4 | 5 | import com.squareup.wire.Message; 6 | import com.squareup.wire.ProtoField; 7 | import java.util.Collections; 8 | import java.util.List; 9 | import okio.ByteString; 10 | 11 | import static com.squareup.wire.Message.Datatype.BOOL; 12 | import static com.squareup.wire.Message.Datatype.BYTES; 13 | import static com.squareup.wire.Message.Datatype.FIXED64; 14 | import static com.squareup.wire.Message.Datatype.INT64; 15 | import static com.squareup.wire.Message.Datatype.STRING; 16 | import static com.squareup.wire.Message.Label.REPEATED; 17 | 18 | public final class Response extends Message { 19 | 20 | public static final Boolean DEFAULT_STATSOK = false; 21 | public static final List DEFAULT_INTENT = Collections.emptyList(); 22 | public static final Long DEFAULT_TIMEMS = 0L; 23 | public static final String DEFAULT_DIGEST = ""; 24 | public static final List DEFAULT_SETTING = Collections.emptyList(); 25 | public static final Boolean DEFAULT_MARKETOK = false; 26 | public static final Long DEFAULT_ANDROIDID = 0L; 27 | public static final Long DEFAULT_SECURITYTOKEN = 0L; 28 | public static final Boolean DEFAULT_SETTINGSDIFF = false; 29 | public static final List DEFAULT_DELETESETTING = Collections.emptyList(); 30 | 31 | @ProtoField(tag = 1, type = BOOL) 32 | public final Boolean statsOk; 33 | 34 | @ProtoField(tag = 2, label = REPEATED) 35 | public final List intent; 36 | 37 | @ProtoField(tag = 3, type = INT64) 38 | public final Long timeMs; 39 | 40 | @ProtoField(tag = 4, type = STRING) 41 | public final String digest; 42 | 43 | @ProtoField(tag = 5, label = REPEATED) 44 | public final List setting; 45 | 46 | @ProtoField(tag = 6, type = BOOL) 47 | public final Boolean marketOk; 48 | 49 | @ProtoField(tag = 7, type = FIXED64) 50 | public final Long androidId; 51 | 52 | @ProtoField(tag = 8, type = FIXED64) 53 | public final Long securityToken; 54 | 55 | @ProtoField(tag = 9, type = BOOL) 56 | public final Boolean settingsDiff; 57 | 58 | @ProtoField(tag = 10, type = STRING, label = REPEATED) 59 | public final List deleteSetting; 60 | 61 | public Response(Boolean statsOk, List intent, Long timeMs, String digest, List setting, Boolean marketOk, Long androidId, Long securityToken, Boolean settingsDiff, List deleteSetting) { 62 | this.statsOk = statsOk; 63 | this.intent = immutableCopyOf(intent); 64 | this.timeMs = timeMs; 65 | this.digest = digest; 66 | this.setting = immutableCopyOf(setting); 67 | this.marketOk = marketOk; 68 | this.androidId = androidId; 69 | this.securityToken = securityToken; 70 | this.settingsDiff = settingsDiff; 71 | this.deleteSetting = immutableCopyOf(deleteSetting); 72 | } 73 | 74 | private Response(Builder builder) { 75 | this(builder.statsOk, builder.intent, builder.timeMs, builder.digest, builder.setting, builder.marketOk, builder.androidId, builder.securityToken, builder.settingsDiff, builder.deleteSetting); 76 | setBuilder(builder); 77 | } 78 | 79 | @Override 80 | public boolean equals(Object other) { 81 | if (other == this) return true; 82 | if (!(other instanceof Response)) return false; 83 | Response o = (Response) other; 84 | return equals(statsOk, o.statsOk) 85 | && equals(intent, o.intent) 86 | && equals(timeMs, o.timeMs) 87 | && equals(digest, o.digest) 88 | && equals(setting, o.setting) 89 | && equals(marketOk, o.marketOk) 90 | && equals(androidId, o.androidId) 91 | && equals(securityToken, o.securityToken) 92 | && equals(settingsDiff, o.settingsDiff) 93 | && equals(deleteSetting, o.deleteSetting); 94 | } 95 | 96 | @Override 97 | public int hashCode() { 98 | int result = hashCode; 99 | if (result == 0) { 100 | result = statsOk != null ? statsOk.hashCode() : 0; 101 | result = result * 37 + (intent != null ? intent.hashCode() : 1); 102 | result = result * 37 + (timeMs != null ? timeMs.hashCode() : 0); 103 | result = result * 37 + (digest != null ? digest.hashCode() : 0); 104 | result = result * 37 + (setting != null ? setting.hashCode() : 1); 105 | result = result * 37 + (marketOk != null ? marketOk.hashCode() : 0); 106 | result = result * 37 + (androidId != null ? androidId.hashCode() : 0); 107 | result = result * 37 + (securityToken != null ? securityToken.hashCode() : 0); 108 | result = result * 37 + (settingsDiff != null ? settingsDiff.hashCode() : 0); 109 | result = result * 37 + (deleteSetting != null ? deleteSetting.hashCode() : 1); 110 | hashCode = result; 111 | } 112 | return result; 113 | } 114 | 115 | public static final class Builder extends Message.Builder { 116 | 117 | public Boolean statsOk; 118 | public List intent; 119 | public Long timeMs; 120 | public String digest; 121 | public List setting; 122 | public Boolean marketOk; 123 | public Long androidId; 124 | public Long securityToken; 125 | public Boolean settingsDiff; 126 | public List deleteSetting; 127 | 128 | public Builder() { 129 | } 130 | 131 | public Builder(Response message) { 132 | super(message); 133 | if (message == null) return; 134 | this.statsOk = message.statsOk; 135 | this.intent = copyOf(message.intent); 136 | this.timeMs = message.timeMs; 137 | this.digest = message.digest; 138 | this.setting = copyOf(message.setting); 139 | this.marketOk = message.marketOk; 140 | this.androidId = message.androidId; 141 | this.securityToken = message.securityToken; 142 | this.settingsDiff = message.settingsDiff; 143 | this.deleteSetting = copyOf(message.deleteSetting); 144 | } 145 | 146 | public Builder statsOk(Boolean statsOk) { 147 | this.statsOk = statsOk; 148 | return this; 149 | } 150 | 151 | public Builder intent(List intent) { 152 | this.intent = checkForNulls(intent); 153 | return this; 154 | } 155 | 156 | public Builder timeMs(Long timeMs) { 157 | this.timeMs = timeMs; 158 | return this; 159 | } 160 | 161 | public Builder digest(String digest) { 162 | this.digest = digest; 163 | return this; 164 | } 165 | 166 | public Builder setting(List setting) { 167 | this.setting = checkForNulls(setting); 168 | return this; 169 | } 170 | 171 | public Builder marketOk(Boolean marketOk) { 172 | this.marketOk = marketOk; 173 | return this; 174 | } 175 | 176 | public Builder androidId(Long androidId) { 177 | this.androidId = androidId; 178 | return this; 179 | } 180 | 181 | public Builder securityToken(Long securityToken) { 182 | this.securityToken = securityToken; 183 | return this; 184 | } 185 | 186 | public Builder settingsDiff(Boolean settingsDiff) { 187 | this.settingsDiff = settingsDiff; 188 | return this; 189 | } 190 | 191 | public Builder deleteSetting(List deleteSetting) { 192 | this.deleteSetting = checkForNulls(deleteSetting); 193 | return this; 194 | } 195 | 196 | @Override 197 | public Response build() { 198 | return new Response(this); 199 | } 200 | } 201 | 202 | public static final class Intent extends Message { 203 | 204 | public static final String DEFAULT_ACTION = ""; 205 | public static final String DEFAULT_DATAURI = ""; 206 | public static final String DEFAULT_MIMETYPE = ""; 207 | public static final String DEFAULT_JAVACLASS = ""; 208 | public static final List DEFAULT_EXTRA = Collections.emptyList(); 209 | 210 | @ProtoField(tag = 1, type = STRING) 211 | public final String action; 212 | 213 | @ProtoField(tag = 2, type = STRING) 214 | public final String dataUri; 215 | 216 | @ProtoField(tag = 3, type = STRING) 217 | public final String mimeType; 218 | 219 | @ProtoField(tag = 4, type = STRING) 220 | public final String javaClass; 221 | 222 | @ProtoField(tag = 5, label = REPEATED) 223 | public final List extra; 224 | 225 | public Intent(String action, String dataUri, String mimeType, String javaClass, List extra) { 226 | this.action = action; 227 | this.dataUri = dataUri; 228 | this.mimeType = mimeType; 229 | this.javaClass = javaClass; 230 | this.extra = immutableCopyOf(extra); 231 | } 232 | 233 | private Intent(Builder builder) { 234 | this(builder.action, builder.dataUri, builder.mimeType, builder.javaClass, builder.extra); 235 | setBuilder(builder); 236 | } 237 | 238 | @Override 239 | public boolean equals(Object other) { 240 | if (other == this) return true; 241 | if (!(other instanceof Intent)) return false; 242 | Intent o = (Intent) other; 243 | return equals(action, o.action) 244 | && equals(dataUri, o.dataUri) 245 | && equals(mimeType, o.mimeType) 246 | && equals(javaClass, o.javaClass) 247 | && equals(extra, o.extra); 248 | } 249 | 250 | @Override 251 | public int hashCode() { 252 | int result = hashCode; 253 | if (result == 0) { 254 | result = action != null ? action.hashCode() : 0; 255 | result = result * 37 + (dataUri != null ? dataUri.hashCode() : 0); 256 | result = result * 37 + (mimeType != null ? mimeType.hashCode() : 0); 257 | result = result * 37 + (javaClass != null ? javaClass.hashCode() : 0); 258 | result = result * 37 + (extra != null ? extra.hashCode() : 1); 259 | hashCode = result; 260 | } 261 | return result; 262 | } 263 | 264 | public static final class Builder extends Message.Builder { 265 | 266 | public String action; 267 | public String dataUri; 268 | public String mimeType; 269 | public String javaClass; 270 | public List extra; 271 | 272 | public Builder() { 273 | } 274 | 275 | public Builder(Intent message) { 276 | super(message); 277 | if (message == null) return; 278 | this.action = message.action; 279 | this.dataUri = message.dataUri; 280 | this.mimeType = message.mimeType; 281 | this.javaClass = message.javaClass; 282 | this.extra = copyOf(message.extra); 283 | } 284 | 285 | public Builder action(String action) { 286 | this.action = action; 287 | return this; 288 | } 289 | 290 | public Builder dataUri(String dataUri) { 291 | this.dataUri = dataUri; 292 | return this; 293 | } 294 | 295 | public Builder mimeType(String mimeType) { 296 | this.mimeType = mimeType; 297 | return this; 298 | } 299 | 300 | public Builder javaClass(String javaClass) { 301 | this.javaClass = javaClass; 302 | return this; 303 | } 304 | 305 | public Builder extra(List extra) { 306 | this.extra = checkForNulls(extra); 307 | return this; 308 | } 309 | 310 | @Override 311 | public Intent build() { 312 | return new Intent(this); 313 | } 314 | } 315 | 316 | public static final class Extra extends Message { 317 | 318 | public static final String DEFAULT_NAME = ""; 319 | public static final String DEFAULT_VALUE = ""; 320 | 321 | @ProtoField(tag = 6, type = STRING) 322 | public final String name; 323 | 324 | @ProtoField(tag = 7, type = STRING) 325 | public final String value; 326 | 327 | public Extra(String name, String value) { 328 | this.name = name; 329 | this.value = value; 330 | } 331 | 332 | private Extra(Builder builder) { 333 | this(builder.name, builder.value); 334 | setBuilder(builder); 335 | } 336 | 337 | @Override 338 | public boolean equals(Object other) { 339 | if (other == this) return true; 340 | if (!(other instanceof Extra)) return false; 341 | Extra o = (Extra) other; 342 | return equals(name, o.name) 343 | && equals(value, o.value); 344 | } 345 | 346 | @Override 347 | public int hashCode() { 348 | int result = hashCode; 349 | if (result == 0) { 350 | result = name != null ? name.hashCode() : 0; 351 | result = result * 37 + (value != null ? value.hashCode() : 0); 352 | hashCode = result; 353 | } 354 | return result; 355 | } 356 | 357 | public static final class Builder extends Message.Builder { 358 | 359 | public String name; 360 | public String value; 361 | 362 | public Builder() { 363 | } 364 | 365 | public Builder(Extra message) { 366 | super(message); 367 | if (message == null) return; 368 | this.name = message.name; 369 | this.value = message.value; 370 | } 371 | 372 | public Builder name(String name) { 373 | this.name = name; 374 | return this; 375 | } 376 | 377 | public Builder value(String value) { 378 | this.value = value; 379 | return this; 380 | } 381 | 382 | @Override 383 | public Extra build() { 384 | return new Extra(this); 385 | } 386 | } 387 | } 388 | } 389 | 390 | public static final class GservicesSetting extends Message { 391 | 392 | public static final ByteString DEFAULT_NAME = ByteString.EMPTY; 393 | public static final ByteString DEFAULT_VALUE = ByteString.EMPTY; 394 | 395 | @ProtoField(tag = 1, type = BYTES) 396 | public final ByteString name; 397 | 398 | @ProtoField(tag = 2, type = BYTES) 399 | public final ByteString value; 400 | 401 | public GservicesSetting(ByteString name, ByteString value) { 402 | this.name = name; 403 | this.value = value; 404 | } 405 | 406 | private GservicesSetting(Builder builder) { 407 | this(builder.name, builder.value); 408 | setBuilder(builder); 409 | } 410 | 411 | @Override 412 | public boolean equals(Object other) { 413 | if (other == this) return true; 414 | if (!(other instanceof GservicesSetting)) return false; 415 | GservicesSetting o = (GservicesSetting) other; 416 | return equals(name, o.name) 417 | && equals(value, o.value); 418 | } 419 | 420 | @Override 421 | public int hashCode() { 422 | int result = hashCode; 423 | if (result == 0) { 424 | result = name != null ? name.hashCode() : 0; 425 | result = result * 37 + (value != null ? value.hashCode() : 0); 426 | hashCode = result; 427 | } 428 | return result; 429 | } 430 | 431 | public static final class Builder extends Message.Builder { 432 | 433 | public ByteString name; 434 | public ByteString value; 435 | 436 | public Builder() { 437 | } 438 | 439 | public Builder(GservicesSetting message) { 440 | super(message); 441 | if (message == null) return; 442 | this.name = message.name; 443 | this.value = message.value; 444 | } 445 | 446 | public Builder name(ByteString name) { 447 | this.name = name; 448 | return this; 449 | } 450 | 451 | public Builder value(ByteString value) { 452 | this.value = value; 453 | return this; 454 | } 455 | 456 | @Override 457 | public GservicesSetting build() { 458 | return new GservicesSetting(this); 459 | } 460 | } 461 | } 462 | } 463 | -------------------------------------------------------------------------------- /src/com/google/tools/Base64.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.tools; 18 | 19 | import java.io.UnsupportedEncodingException; 20 | 21 | /** 22 | * Utilities for encoding and decoding the Base64 representation of 23 | * binary data. See RFCs 2045 and 3548. 26 | */ 27 | public class Base64 { 28 | /** 29 | * Default values for encoder/decoder flags. 30 | */ 31 | public static final int DEFAULT = 0; 32 | 33 | /** 34 | * Encoder flag bit to omit the padding '=' characters at the end 35 | * of the output (if any). 36 | */ 37 | public static final int NO_PADDING = 1; 38 | 39 | /** 40 | * Encoder flag bit to omit all line terminators (i.e., the output 41 | * will be on one long line). 42 | */ 43 | public static final int NO_WRAP = 2; 44 | 45 | /** 46 | * Encoder flag bit to indicate lines should be terminated with a 47 | * CRLF pair instead of just an LF. Has no effect if {@code 48 | * NO_WRAP} is specified as well. 49 | */ 50 | public static final int CRLF = 4; 51 | 52 | /** 53 | * Encoder/decoder flag bit to indicate using the "URL and 54 | * filename safe" variant of Base64 (see RFC 3548 section 4) where 55 | * {@code -} and {@code _} are used in place of {@code +} and 56 | * {@code /}. 57 | */ 58 | public static final int URL_SAFE = 8; 59 | 60 | /** 61 | * Flag to pass to {@link Base64OutputStream} to indicate that it 62 | * should not close the output stream it is wrapping when it 63 | * itself is closed. 64 | */ 65 | public static final int NO_CLOSE = 16; 66 | 67 | // -------------------------------------------------------- 68 | // shared code 69 | // -------------------------------------------------------- 70 | 71 | /* package */ static abstract class Coder { 72 | public byte[] output; 73 | public int op; 74 | 75 | /** 76 | * Encode/decode another block of input data. this.output is 77 | * provided by the caller, and must be big enough to hold all 78 | * the coded data. On exit, this.opwill be set to the length 79 | * of the coded data. 80 | * 81 | * @param finish true if this is the final call to process for 82 | * this object. Will finalize the coder state and 83 | * include any final bytes in the output. 84 | * 85 | * @return true if the input so far is good; false if some 86 | * error has been detected in the input stream.. 87 | */ 88 | public abstract boolean process(byte[] input, int offset, int len, boolean finish); 89 | 90 | /** 91 | * @return the maximum number of bytes a call to process() 92 | * could produce for the given number of input bytes. This may 93 | * be an overestimate. 94 | */ 95 | public abstract int maxOutputSize(int len); 96 | } 97 | 98 | // -------------------------------------------------------- 99 | // decoding 100 | // -------------------------------------------------------- 101 | 102 | /** 103 | * Decode the Base64-encoded data in input and return the data in 104 | * a new byte array. 105 | * 106 | *

The padding '=' characters at the end are considered optional, but 107 | * if any are present, there must be the correct number of them. 108 | * 109 | * @param str the input String to decode, which is converted to 110 | * bytes using the default charset 111 | * @param flags controls certain features of the decoded output. 112 | * Pass {@code DEFAULT} to decode standard Base64. 113 | * 114 | * @throws IllegalArgumentException if the input contains 115 | * incorrect padding 116 | */ 117 | public static byte[] decode(String str, int flags) { 118 | return decode(str.getBytes(), flags); 119 | } 120 | 121 | /** 122 | * Decode the Base64-encoded data in input and return the data in 123 | * a new byte array. 124 | * 125 | *

The padding '=' characters at the end are considered optional, but 126 | * if any are present, there must be the correct number of them. 127 | * 128 | * @param input the input array to decode 129 | * @param flags controls certain features of the decoded output. 130 | * Pass {@code DEFAULT} to decode standard Base64. 131 | * 132 | * @throws IllegalArgumentException if the input contains 133 | * incorrect padding 134 | */ 135 | public static byte[] decode(byte[] input, int flags) { 136 | return decode(input, 0, input.length, flags); 137 | } 138 | 139 | /** 140 | * Decode the Base64-encoded data in input and return the data in 141 | * a new byte array. 142 | * 143 | *

The padding '=' characters at the end are considered optional, but 144 | * if any are present, there must be the correct number of them. 145 | * 146 | * @param input the data to decode 147 | * @param offset the position within the input array at which to start 148 | * @param len the number of bytes of input to decode 149 | * @param flags controls certain features of the decoded output. 150 | * Pass {@code DEFAULT} to decode standard Base64. 151 | * 152 | * @throws IllegalArgumentException if the input contains 153 | * incorrect padding 154 | */ 155 | public static byte[] decode(byte[] input, int offset, int len, int flags) { 156 | // Allocate space for the most data the input could represent. 157 | // (It could contain less if it contains whitespace, etc.) 158 | Decoder decoder = new Decoder(flags, new byte[len*3/4]); 159 | 160 | if (!decoder.process(input, offset, len, true)) { 161 | throw new IllegalArgumentException("bad base-64"); 162 | } 163 | 164 | // Maybe we got lucky and allocated exactly enough output space. 165 | if (decoder.op == decoder.output.length) { 166 | return decoder.output; 167 | } 168 | 169 | // Need to shorten the array, so allocate a new one of the 170 | // right size and copy. 171 | byte[] temp = new byte[decoder.op]; 172 | System.arraycopy(decoder.output, 0, temp, 0, decoder.op); 173 | return temp; 174 | } 175 | 176 | /* package */ static class Decoder extends Coder { 177 | /** 178 | * Lookup table for turning bytes into their position in the 179 | * Base64 alphabet. 180 | */ 181 | private static final int DECODE[] = { 182 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 183 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 184 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 185 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, 186 | -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 187 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, 188 | -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 189 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, 190 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 191 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 192 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 193 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 194 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 195 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 196 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 197 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 198 | }; 199 | 200 | /** 201 | * Decode lookup table for the "web safe" variant (RFC 3548 202 | * sec. 4) where - and _ replace + and /. 203 | */ 204 | private static final int DECODE_WEBSAFE[] = { 205 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 206 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 207 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, 208 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, 209 | -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 210 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, 211 | -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 212 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, 213 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 214 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 215 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 216 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 217 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 218 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 219 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 220 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 221 | }; 222 | 223 | /** Non-data values in the DECODE arrays. */ 224 | private static final int SKIP = -1; 225 | private static final int EQUALS = -2; 226 | 227 | /** 228 | * States 0-3 are reading through the next input tuple. 229 | * State 4 is having read one '=' and expecting exactly 230 | * one more. 231 | * State 5 is expecting no more data or padding characters 232 | * in the input. 233 | * State 6 is the error state; an error has been detected 234 | * in the input and no future input can "fix" it. 235 | */ 236 | private int state; // state number (0 to 6) 237 | private int value; 238 | 239 | final private int[] alphabet; 240 | 241 | public Decoder(int flags, byte[] output) { 242 | this.output = output; 243 | 244 | alphabet = ((flags & URL_SAFE) == 0) ? DECODE : DECODE_WEBSAFE; 245 | state = 0; 246 | value = 0; 247 | } 248 | 249 | /** 250 | * @return an overestimate for the number of bytes {@code 251 | * len} bytes could decode to. 252 | */ 253 | public int maxOutputSize(int len) { 254 | return len * 3/4 + 10; 255 | } 256 | 257 | /** 258 | * Decode another block of input data. 259 | * 260 | * @return true if the state machine is still healthy. false if 261 | * bad base-64 data has been detected in the input stream. 262 | */ 263 | public boolean process(byte[] input, int offset, int len, boolean finish) { 264 | if (this.state == 6) return false; 265 | 266 | int p = offset; 267 | len += offset; 268 | 269 | // Using local variables makes the decoder about 12% 270 | // faster than if we manipulate the member variables in 271 | // the loop. (Even alphabet makes a measurable 272 | // difference, which is somewhat surprising to me since 273 | // the member variable is final.) 274 | int state = this.state; 275 | int value = this.value; 276 | int op = 0; 277 | final byte[] output = this.output; 278 | final int[] alphabet = this.alphabet; 279 | 280 | while (p < len) { 281 | // Try the fast path: we're starting a new tuple and the 282 | // next four bytes of the input stream are all data 283 | // bytes. This corresponds to going through states 284 | // 0-1-2-3-0. We expect to use this method for most of 285 | // the data. 286 | // 287 | // If any of the next four bytes of input are non-data 288 | // (whitespace, etc.), value will end up negative. (All 289 | // the non-data values in decode are small negative 290 | // numbers, so shifting any of them up and or'ing them 291 | // together will result in a value with its top bit set.) 292 | // 293 | // You can remove this whole block and the output should 294 | // be the same, just slower. 295 | if (state == 0) { 296 | while (p+4 <= len && 297 | (value = ((alphabet[input[p] & 0xff] << 18) | 298 | (alphabet[input[p+1] & 0xff] << 12) | 299 | (alphabet[input[p+2] & 0xff] << 6) | 300 | (alphabet[input[p+3] & 0xff]))) >= 0) { 301 | output[op+2] = (byte) value; 302 | output[op+1] = (byte) (value >> 8); 303 | output[op] = (byte) (value >> 16); 304 | op += 3; 305 | p += 4; 306 | } 307 | if (p >= len) break; 308 | } 309 | 310 | // The fast path isn't available -- either we've read a 311 | // partial tuple, or the next four input bytes aren't all 312 | // data, or whatever. Fall back to the slower state 313 | // machine implementation. 314 | 315 | int d = alphabet[input[p++] & 0xff]; 316 | 317 | switch (state) { 318 | case 0: 319 | if (d >= 0) { 320 | value = d; 321 | ++state; 322 | } else if (d != SKIP) { 323 | this.state = 6; 324 | return false; 325 | } 326 | break; 327 | 328 | case 1: 329 | if (d >= 0) { 330 | value = (value << 6) | d; 331 | ++state; 332 | } else if (d != SKIP) { 333 | this.state = 6; 334 | return false; 335 | } 336 | break; 337 | 338 | case 2: 339 | if (d >= 0) { 340 | value = (value << 6) | d; 341 | ++state; 342 | } else if (d == EQUALS) { 343 | // Emit the last (partial) output tuple; 344 | // expect exactly one more padding character. 345 | output[op++] = (byte) (value >> 4); 346 | state = 4; 347 | } else if (d != SKIP) { 348 | this.state = 6; 349 | return false; 350 | } 351 | break; 352 | 353 | case 3: 354 | if (d >= 0) { 355 | // Emit the output triple and return to state 0. 356 | value = (value << 6) | d; 357 | output[op+2] = (byte) value; 358 | output[op+1] = (byte) (value >> 8); 359 | output[op] = (byte) (value >> 16); 360 | op += 3; 361 | state = 0; 362 | } else if (d == EQUALS) { 363 | // Emit the last (partial) output tuple; 364 | // expect no further data or padding characters. 365 | output[op+1] = (byte) (value >> 2); 366 | output[op] = (byte) (value >> 10); 367 | op += 2; 368 | state = 5; 369 | } else if (d != SKIP) { 370 | this.state = 6; 371 | return false; 372 | } 373 | break; 374 | 375 | case 4: 376 | if (d == EQUALS) { 377 | ++state; 378 | } else if (d != SKIP) { 379 | this.state = 6; 380 | return false; 381 | } 382 | break; 383 | 384 | case 5: 385 | if (d != SKIP) { 386 | this.state = 6; 387 | return false; 388 | } 389 | break; 390 | } 391 | } 392 | 393 | if (!finish) { 394 | // We're out of input, but a future call could provide 395 | // more. 396 | this.state = state; 397 | this.value = value; 398 | this.op = op; 399 | return true; 400 | } 401 | 402 | // Done reading input. Now figure out where we are left in 403 | // the state machine and finish up. 404 | 405 | switch (state) { 406 | case 0: 407 | // Output length is a multiple of three. Fine. 408 | break; 409 | case 1: 410 | // Read one extra input byte, which isn't enough to 411 | // make another output byte. Illegal. 412 | this.state = 6; 413 | return false; 414 | case 2: 415 | // Read two extra input bytes, enough to emit 1 more 416 | // output byte. Fine. 417 | output[op++] = (byte) (value >> 4); 418 | break; 419 | case 3: 420 | // Read three extra input bytes, enough to emit 2 more 421 | // output bytes. Fine. 422 | output[op++] = (byte) (value >> 10); 423 | output[op++] = (byte) (value >> 2); 424 | break; 425 | case 4: 426 | // Read one padding '=' when we expected 2. Illegal. 427 | this.state = 6; 428 | return false; 429 | case 5: 430 | // Read all the padding '='s we expected and no more. 431 | // Fine. 432 | break; 433 | } 434 | 435 | this.state = state; 436 | this.op = op; 437 | return true; 438 | } 439 | } 440 | 441 | // -------------------------------------------------------- 442 | // encoding 443 | // -------------------------------------------------------- 444 | 445 | /** 446 | * Base64-encode the given data and return a newly allocated 447 | * String with the result. 448 | * 449 | * @param input the data to encode 450 | * @param flags controls certain features of the encoded output. 451 | * Passing {@code DEFAULT} results in output that 452 | * adheres to RFC 2045. 453 | */ 454 | public static String encodeToString(byte[] input, int flags) { 455 | try { 456 | return new String(encode(input, flags), "US-ASCII"); 457 | } catch (UnsupportedEncodingException e) { 458 | // US-ASCII is guaranteed to be available. 459 | throw new AssertionError(e); 460 | } 461 | } 462 | 463 | /** 464 | * Base64-encode the given data and return a newly allocated 465 | * String with the result. 466 | * 467 | * @param input the data to encode 468 | * @param offset the position within the input array at which to 469 | * start 470 | * @param len the number of bytes of input to encode 471 | * @param flags controls certain features of the encoded output. 472 | * Passing {@code DEFAULT} results in output that 473 | * adheres to RFC 2045. 474 | */ 475 | public static String encodeToString(byte[] input, int offset, int len, int flags) { 476 | try { 477 | return new String(encode(input, offset, len, flags), "US-ASCII"); 478 | } catch (UnsupportedEncodingException e) { 479 | // US-ASCII is guaranteed to be available. 480 | throw new AssertionError(e); 481 | } 482 | } 483 | 484 | /** 485 | * Base64-encode the given data and return a newly allocated 486 | * byte[] with the result. 487 | * 488 | * @param input the data to encode 489 | * @param flags controls certain features of the encoded output. 490 | * Passing {@code DEFAULT} results in output that 491 | * adheres to RFC 2045. 492 | */ 493 | public static byte[] encode(byte[] input, int flags) { 494 | return encode(input, 0, input.length, flags); 495 | } 496 | 497 | /** 498 | * Base64-encode the given data and return a newly allocated 499 | * byte[] with the result. 500 | * 501 | * @param input the data to encode 502 | * @param offset the position within the input array at which to 503 | * start 504 | * @param len the number of bytes of input to encode 505 | * @param flags controls certain features of the encoded output. 506 | * Passing {@code DEFAULT} results in output that 507 | * adheres to RFC 2045. 508 | */ 509 | public static byte[] encode(byte[] input, int offset, int len, int flags) { 510 | Encoder encoder = new Encoder(flags, null); 511 | 512 | // Compute the exact length of the array we will produce. 513 | int output_len = len / 3 * 4; 514 | 515 | // Account for the tail of the data and the padding bytes, if any. 516 | if (encoder.do_padding) { 517 | if (len % 3 > 0) { 518 | output_len += 4; 519 | } 520 | } else { 521 | switch (len % 3) { 522 | case 0: break; 523 | case 1: output_len += 2; break; 524 | case 2: output_len += 3; break; 525 | } 526 | } 527 | 528 | // Account for the newlines, if any. 529 | if (encoder.do_newline && len > 0) { 530 | output_len += (((len-1) / (3 * Encoder.LINE_GROUPS)) + 1) * 531 | (encoder.do_cr ? 2 : 1); 532 | } 533 | 534 | encoder.output = new byte[output_len]; 535 | encoder.process(input, offset, len, true); 536 | 537 | assert encoder.op == output_len; 538 | 539 | return encoder.output; 540 | } 541 | 542 | /* package */ static class Encoder extends Coder { 543 | /** 544 | * Emit a new line every this many output tuples. Corresponds to 545 | * a 76-character line length (the maximum allowable according to 546 | * RFC 2045). 547 | */ 548 | public static final int LINE_GROUPS = 19; 549 | 550 | /** 551 | * Lookup table for turning Base64 alphabet positions (6 bits) 552 | * into output bytes. 553 | */ 554 | private static final byte ENCODE[] = { 555 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 556 | 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 557 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 558 | 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', 559 | }; 560 | 561 | /** 562 | * Lookup table for turning Base64 alphabet positions (6 bits) 563 | * into output bytes. 564 | */ 565 | private static final byte ENCODE_WEBSAFE[] = { 566 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 567 | 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 568 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 569 | 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', 570 | }; 571 | 572 | final private byte[] tail; 573 | /* package */ int tailLen; 574 | private int count; 575 | 576 | final public boolean do_padding; 577 | final public boolean do_newline; 578 | final public boolean do_cr; 579 | final private byte[] alphabet; 580 | 581 | public Encoder(int flags, byte[] output) { 582 | this.output = output; 583 | 584 | do_padding = (flags & NO_PADDING) == 0; 585 | do_newline = (flags & NO_WRAP) == 0; 586 | do_cr = (flags & CRLF) != 0; 587 | alphabet = ((flags & URL_SAFE) == 0) ? ENCODE : ENCODE_WEBSAFE; 588 | 589 | tail = new byte[2]; 590 | tailLen = 0; 591 | 592 | count = do_newline ? LINE_GROUPS : -1; 593 | } 594 | 595 | /** 596 | * @return an overestimate for the number of bytes {@code 597 | * len} bytes could encode to. 598 | */ 599 | public int maxOutputSize(int len) { 600 | return len * 8/5 + 10; 601 | } 602 | 603 | public boolean process(byte[] input, int offset, int len, boolean finish) { 604 | // Using local variables makes the encoder about 9% faster. 605 | final byte[] alphabet = this.alphabet; 606 | final byte[] output = this.output; 607 | int op = 0; 608 | int count = this.count; 609 | 610 | int p = offset; 611 | len += offset; 612 | int v = -1; 613 | 614 | // First we need to concatenate the tail of the previous call 615 | // with any input bytes available now and see if we can empty 616 | // the tail. 617 | 618 | switch (tailLen) { 619 | case 0: 620 | // There was no tail. 621 | break; 622 | 623 | case 1: 624 | if (p+2 <= len) { 625 | // A 1-byte tail with at least 2 bytes of 626 | // input available now. 627 | v = ((tail[0] & 0xff) << 16) | 628 | ((input[p++] & 0xff) << 8) | 629 | (input[p++] & 0xff); 630 | tailLen = 0; 631 | }; 632 | break; 633 | 634 | case 2: 635 | if (p+1 <= len) { 636 | // A 2-byte tail with at least 1 byte of input. 637 | v = ((tail[0] & 0xff) << 16) | 638 | ((tail[1] & 0xff) << 8) | 639 | (input[p++] & 0xff); 640 | tailLen = 0; 641 | } 642 | break; 643 | } 644 | 645 | if (v != -1) { 646 | output[op++] = alphabet[(v >> 18) & 0x3f]; 647 | output[op++] = alphabet[(v >> 12) & 0x3f]; 648 | output[op++] = alphabet[(v >> 6) & 0x3f]; 649 | output[op++] = alphabet[v & 0x3f]; 650 | if (--count == 0) { 651 | if (do_cr) output[op++] = '\r'; 652 | output[op++] = '\n'; 653 | count = LINE_GROUPS; 654 | } 655 | } 656 | 657 | // At this point either there is no tail, or there are fewer 658 | // than 3 bytes of input available. 659 | 660 | // The main loop, turning 3 input bytes into 4 output bytes on 661 | // each iteration. 662 | while (p+3 <= len) { 663 | v = ((input[p] & 0xff) << 16) | 664 | ((input[p+1] & 0xff) << 8) | 665 | (input[p+2] & 0xff); 666 | output[op] = alphabet[(v >> 18) & 0x3f]; 667 | output[op+1] = alphabet[(v >> 12) & 0x3f]; 668 | output[op+2] = alphabet[(v >> 6) & 0x3f]; 669 | output[op+3] = alphabet[v & 0x3f]; 670 | p += 3; 671 | op += 4; 672 | if (--count == 0) { 673 | if (do_cr) output[op++] = '\r'; 674 | output[op++] = '\n'; 675 | count = LINE_GROUPS; 676 | } 677 | } 678 | 679 | if (finish) { 680 | // Finish up the tail of the input. Note that we need to 681 | // consume any bytes in tail before any bytes 682 | // remaining in input; there should be at most two bytes 683 | // total. 684 | 685 | if (p-tailLen == len-1) { 686 | int t = 0; 687 | v = ((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 4; 688 | tailLen -= t; 689 | output[op++] = alphabet[(v >> 6) & 0x3f]; 690 | output[op++] = alphabet[v & 0x3f]; 691 | if (do_padding) { 692 | output[op++] = '='; 693 | output[op++] = '='; 694 | } 695 | if (do_newline) { 696 | if (do_cr) output[op++] = '\r'; 697 | output[op++] = '\n'; 698 | } 699 | } else if (p-tailLen == len-2) { 700 | int t = 0; 701 | v = (((tailLen > 1 ? tail[t++] : input[p++]) & 0xff) << 10) | 702 | (((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 2); 703 | tailLen -= t; 704 | output[op++] = alphabet[(v >> 12) & 0x3f]; 705 | output[op++] = alphabet[(v >> 6) & 0x3f]; 706 | output[op++] = alphabet[v & 0x3f]; 707 | if (do_padding) { 708 | output[op++] = '='; 709 | } 710 | if (do_newline) { 711 | if (do_cr) output[op++] = '\r'; 712 | output[op++] = '\n'; 713 | } 714 | } else if (do_newline && op > 0 && count != LINE_GROUPS) { 715 | if (do_cr) output[op++] = '\r'; 716 | output[op++] = '\n'; 717 | } 718 | 719 | assert tailLen == 0; 720 | assert p == len; 721 | } else { 722 | // Save the leftovers in tail to be consumed on the next 723 | // call to encodeInternal. 724 | 725 | if (p == len-1) { 726 | tail[tailLen++] = input[p]; 727 | } else if (p == len-2) { 728 | tail[tailLen++] = input[p]; 729 | tail[tailLen++] = input[p+1]; 730 | } 731 | } 732 | 733 | this.op = op; 734 | this.count = count; 735 | 736 | return true; 737 | } 738 | } 739 | 740 | private Base64() { } // don't instantiate 741 | } 742 | -------------------------------------------------------------------------------- /src/com/google/tools/Client.java: -------------------------------------------------------------------------------- 1 | package com.google.tools; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.OutputStream; 7 | import java.net.HttpURLConnection; 8 | import java.net.ProtocolException; 9 | import java.util.zip.GZIPInputStream; 10 | import java.util.zip.GZIPOutputStream; 11 | 12 | public abstract class Client { 13 | public static final String KEY_HTTP_USER_AGENT = "userAgent"; 14 | 15 | protected static final String REQUEST_CONTENT_TYPE_FIELD = "Content-Type"; 16 | protected static final String REQUEST_CONTENT_ENCODING_FIELD = "Content-Encoding"; 17 | protected static final String REQUEST_USER_AGENT_FIELD = "User-Agent"; 18 | protected static final String REQUEST_ACCEPT_FIELD = "Accept"; 19 | protected static final String REQUEST_ACCEPT_ENCODING_FIELD = "Accept-Encoding"; 20 | 21 | public static boolean DEBUG = false; 22 | public static boolean DEBUG_ERROR = true; 23 | public static boolean DEBUG_HEADER = false; 24 | 25 | protected String requestContentType = "application/x-www-form-urlencoded; charset=UTF-8"; 26 | protected String requestMethod = "POST"; 27 | 28 | protected boolean isError(final HttpURLConnection connection) { 29 | try { 30 | if (connection.getResponseCode() != 200) { 31 | if (DEBUG_ERROR) { 32 | System.out 33 | .println("Error: " + connection.getResponseCode() + " " + connection.getResponseMessage()); 34 | } 35 | return true; 36 | } 37 | } catch (final IOException e) { 38 | return true; 39 | } 40 | return false; 41 | } 42 | 43 | protected void prepareConnection(final HttpURLConnection connection, final boolean gzip) { 44 | try { 45 | connection.setRequestMethod(requestMethod); 46 | } catch (final ProtocolException e) { 47 | if (DEBUG_ERROR) { 48 | System.out.println("Could not enable POST-Request"); 49 | } 50 | throw new RuntimeException("Could not enable POST-Request", e); 51 | } 52 | connection.setRequestProperty(REQUEST_CONTENT_TYPE_FIELD, requestContentType); 53 | //connection.setRequestProperty(REQUEST_ACCEPT_ENCODING_FIELD, gzip ? "gzip" : "identity"); 54 | if (gzip) { 55 | connection.setRequestProperty(REQUEST_CONTENT_ENCODING_FIELD, "gzip"); 56 | } 57 | connection.setUseCaches(false); 58 | connection.setDoInput(true); 59 | connection.setDoOutput(true); 60 | } 61 | 62 | protected byte[] readData(final HttpURLConnection connection, final boolean gunzip) { 63 | return readData(connection, isError(connection), gunzip); 64 | } 65 | 66 | protected byte[] readData(final HttpURLConnection connection, final boolean error, final boolean gunzip) { 67 | try { 68 | InputStream is = null; 69 | if (error) { 70 | is = connection.getErrorStream(); 71 | } else { 72 | is = connection.getInputStream(); 73 | if (gunzip) { 74 | is = new GZIPInputStream(is); 75 | } 76 | } 77 | if (is == null) { 78 | if (DEBUG) { 79 | System.out.println("InputStream is null ?!"); 80 | } 81 | } 82 | return readStreamToEnd(is); 83 | } catch (final Exception e) { 84 | if (DEBUG_ERROR) { 85 | System.out.println("Could not read data!"); 86 | } 87 | throw new RuntimeException("Could not read data", e); 88 | } 89 | } 90 | 91 | protected void setUserAgent(HttpURLConnection connection, RequestContext info) { 92 | connection.setRequestProperty(REQUEST_USER_AGENT_FIELD, info.getString(KEY_HTTP_USER_AGENT)); 93 | } 94 | 95 | protected void setUserAgent(HttpURLConnection connection, String userAgent) { 96 | connection.setRequestProperty(REQUEST_USER_AGENT_FIELD, userAgent); 97 | } 98 | 99 | protected byte[] readStreamToEnd(final InputStream is) throws IOException { 100 | final ByteArrayOutputStream bos = new ByteArrayOutputStream(); 101 | if (is != null) { 102 | final byte[] buff = new byte[1024]; 103 | while (true) { 104 | final int nb = is.read(buff); 105 | if (nb < 0) { 106 | break; 107 | } 108 | bos.write(buff, 0, nb); 109 | } 110 | is.close(); 111 | } 112 | return bos.toByteArray(); 113 | } 114 | 115 | protected void beforeRequest(final HttpURLConnection connection) { 116 | if (DEBUG_HEADER) { 117 | for (String key : connection.getRequestProperties().keySet()) { 118 | System.out.println("Header | " + key + ": " + connection.getRequestProperty(key)); 119 | } 120 | } 121 | } 122 | 123 | protected void writeData(final HttpURLConnection connection, final byte[] bytes, final boolean gzip) { 124 | beforeRequest(connection); 125 | try { 126 | OutputStream os = connection.getOutputStream(); 127 | if (gzip) { 128 | os = new GZIPOutputStream(os); 129 | } 130 | /* 131 | * final DataOutputStream stream = new DataOutputStream(os); 132 | * stream.writeBytes(string); 133 | */ 134 | os.write(bytes); 135 | os.flush(); 136 | os.close(); 137 | } catch (final IOException e) { 138 | if (DEBUG_ERROR) { 139 | System.out.println("Could not send data!"); 140 | e.printStackTrace(); 141 | } 142 | throw new RuntimeException("Could not send data", e); 143 | } 144 | } 145 | 146 | protected void writeData(final HttpURLConnection connection, final String string, final boolean gzip) { 147 | if (DEBUG) { 148 | System.out.println("Sending" + (gzip ? "(gzipped)" : "") + ": " + string); 149 | } 150 | writeData(connection, string.getBytes(), gzip); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/com/google/tools/RequestContext.java: -------------------------------------------------------------------------------- 1 | package com.google.tools; 2 | 3 | import com.google.android.AndroidContext; 4 | 5 | import java.util.*; 6 | import java.util.regex.Pattern; 7 | 8 | public class RequestContext { 9 | public static final Pattern FALSE_PATTERN = Pattern.compile("^(0|false|f|off|no|n)$", Pattern.CASE_INSENSITIVE); 10 | public static final Pattern TRUE_PATTERN = Pattern.compile("^(1|true|t|on|yes|y)$", Pattern.CASE_INSENSITIVE); 11 | private final HashMap data; 12 | 13 | public RequestContext() { 14 | data = new HashMap(); 15 | } 16 | 17 | public RequestContext(Map m) { 18 | data = new HashMap(m); 19 | } 20 | 21 | public RequestContext(RequestContext ctx) { 22 | this(ctx.data); 23 | } 24 | 25 | public Long getLong(String key) { 26 | try { 27 | return Long.parseLong(getString(key)); 28 | } catch (NumberFormatException e) { 29 | return null; 30 | } 31 | } 32 | 33 | public String getString(Object key) { 34 | return (String) data.get(key); 35 | } 36 | 37 | public Integer getInt(String key) { 38 | try { 39 | return Integer.parseInt(getString(key)); 40 | } catch (NumberFormatException e) { 41 | return null; 42 | } 43 | } 44 | 45 | public T set(String key, int value) { 46 | return set(key, Integer.toString(value)); 47 | } 48 | 49 | public T set(String key, Long value) { 50 | 51 | 52 | if (value == null) { 53 | data.remove(key); 54 | return (T) this; 55 | } 56 | return set(key, Long.toString(value)); 57 | } 58 | 59 | public T set(String key, long value) { 60 | return set(key, Long.toString(value)); 61 | } 62 | 63 | public T set(String key, boolean bool) { 64 | return set(key, bool ? 1 : 0); 65 | } 66 | 67 | public T get(String key, T defaultValue) { 68 | if (data.containsKey(key) && data.get(key) != null) { 69 | try { 70 | return (T) data.get(key); 71 | } catch (ClassCastException ignored) { 72 | } 73 | } 74 | return defaultValue; 75 | } 76 | 77 | public T set(String key, Object value) { 78 | if (value != null && !(value instanceof String) && !(value instanceof Collection)) { 79 | System.out.println("Trying to store value of class " + value.getClass()); 80 | } else { 81 | data.put(key, value); 82 | } 83 | return (T) this; 84 | } 85 | 86 | public Boolean getBoolean(String key, boolean defaultValue) { 87 | String string = getString(key); 88 | boolean res = defaultValue; 89 | if ((string != null) && !string.isEmpty()) { 90 | if (TRUE_PATTERN.matcher(string).matches()) { 91 | res = true; 92 | } else if (FALSE_PATTERN.matcher(string).matches()) { 93 | res = false; 94 | } 95 | } 96 | return res; 97 | } 98 | 99 | public int getInt(String key, int defaultValue) { 100 | try { 101 | return getInt(key); 102 | } catch (Exception ignored) { 103 | return defaultValue; 104 | } 105 | } 106 | 107 | public long getLong(String key, long defaultValue) { 108 | try { 109 | return getLong(key); 110 | } catch (Exception ignored) { 111 | return defaultValue; 112 | } 113 | } 114 | 115 | @Override 116 | public String toString() { 117 | StringBuilder sb = new StringBuilder("RequestContext{\r\n"); 118 | ArrayList strings = new ArrayList(data.keySet()); 119 | Collections.sort(strings); 120 | for (String key : strings) { 121 | sb.append("\t").append(key).append("=").append(data.get(key)).append("\r\n"); 122 | } 123 | return sb.append("}").toString(); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/com/google/tools/SignatureTools.java: -------------------------------------------------------------------------------- 1 | package com.google.tools; 2 | 3 | import java.security.MessageDigest; 4 | import java.security.NoSuchAlgorithmException; 5 | 6 | public class SignatureTools { 7 | 8 | public static String secureSHA1hash(byte[] signatureBytes) { 9 | MessageDigest md; 10 | try { 11 | md = MessageDigest.getInstance("SHA-1"); 12 | } catch (final NoSuchAlgorithmException e) { 13 | return null; 14 | } 15 | if (md != null) { 16 | signatureBytes = md.digest(signatureBytes); 17 | if (signatureBytes != null) { 18 | try { 19 | return noPadding(Base64.encodeToString(signatureBytes, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP)); 20 | } catch (Exception e) { 21 | } 22 | } 23 | } 24 | return null; 25 | } 26 | 27 | private static String noPadding(String base64) { 28 | if (base64 == null) 29 | return null; 30 | if (base64.endsWith("=")) { 31 | base64 = base64.substring(0, base64.length() - 1); 32 | if (base64.endsWith("=")) { 33 | base64 = base64.substring(0, base64.length() - 1); 34 | } 35 | } 36 | return base64; 37 | } 38 | 39 | public static String secureMD5hash(byte[] signatureBytes) { 40 | MessageDigest md; 41 | try { 42 | md = MessageDigest.getInstance("MD5"); 43 | } catch (final NoSuchAlgorithmException e) { 44 | return null; 45 | } 46 | if (md != null) { 47 | signatureBytes = md.digest(signatureBytes); 48 | if (signatureBytes != null) { 49 | try { 50 | return noPadding(Base64.encodeToString(signatureBytes, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP)); 51 | } catch (Exception e) { 52 | } 53 | } 54 | } 55 | return null; 56 | } 57 | 58 | public static String signatureDigest(byte[] signatureBytes) { 59 | MessageDigest md; 60 | try { 61 | md = MessageDigest.getInstance("SHA1"); 62 | } catch (final NoSuchAlgorithmException e) { 63 | return null; 64 | } 65 | if (md != null) { 66 | signatureBytes = md.digest(signatureBytes); 67 | if (signatureBytes != null) { 68 | return toHex(signatureBytes); 69 | } 70 | } 71 | return null; 72 | } 73 | 74 | public static String toHex(final byte buffer[]) { 75 | final StringBuffer stringbuffer = new StringBuffer(2 * buffer.length); 76 | for (final byte b : buffer) { 77 | final Object aobj[] = new Object[1]; 78 | aobj[0] = Byte.valueOf(b); 79 | stringbuffer.append(String.format("%02x", aobj)); 80 | } 81 | return stringbuffer.toString(); 82 | } 83 | } 84 | --------------------------------------------------------------------------------