def _purchase_free(self, app, data): """"Purchases" an application. Google Play requires this even for free applications. Basically, it is used to associate the application with the provided account. Additionally, this returns a download token required to get the app from the server. Parameters ---------- app : App The app to purchase. data : dict Parameters required for a successful query. Returns ------- str A download token required to access the app on the server. """ response = verbose_post(SERVER + 'fdfe/purchase', data=data, headers=self._base_headers()) proto_response = ResponseWrapper.FromString(response.content) error = proto_response.commands.displayErrorMessage if error == "": return proto_response.payload.buyResponse.downloadToken elif error == 'Can\'t install. Please try again later.': raise Retry(app.package_name()) elif 'busy' in error: raise Wait(app.package_name()) else: raise RequestError(error)
def _upload_device_configuration(self): """Uploads the device configuration to Google Play. Necessary for any login as we simulate an Android device. """ LOGGER.info(f'Uploading Device Configuration for {self.device.userreadablename}') upload = UploadDeviceConfigRequest() upload.deviceConfiguration.CopyFrom(self.device.get_device_config()) headers = self._base_headers() headers['X-DFE-Enabled-Experiments'] = "cl:billing.select_add_instrument_by_default" headers['X-DFE-Unsupported-Experiments'] = ('nocache:billing.use_charging_poller,' 'market_emails,buyer_currency,prod_baseline,' 'checkin.set_asset_paid_app_field, ' 'shekel_test,content_ratings,buyer_currency_in_app,' 'nocache:encrypted_apk,recent_changes') headers['X-DFE-SmallestScreenWidthDp'] = "320" headers['X-DFE-Filter-Level'] = "3" data = upload.SerializeToString() response = verbose_post('https://android.clients.google.com/fdfe/uploadDeviceConfig', data=data, headers=headers) proto_response = ResponseWrapper.FromString(response.content) try: if proto_response.payload.HasField('uploadDeviceConfigResponse'): self.device_config_token = proto_response.payload.uploadDeviceConfigResponse.uploadDeviceConfigToken except ValueError: pass
def _issue_checkin(self, ac2dm_token): """Perform a checkin (two actually), which is necessary to complete a login to Google Play. Parameters ---------- ac2dm_token : str An ac2dm token issued by Google Play. Returns ------- str An Android ID. """ headers = self._base_headers() headers['Content-Type'] = 'application/x-protobuf' request = AndroidCheckinRequest() request.id = 0 request.checkin.CopyFrom(self.device.get_checkin()) request.locale = 'em_US' request.timeZone = 'UTC' request.version = 3 request.deviceConfiguration.CopyFrom(self.device.get_device_config()) request.fragment = 0 data = request.SerializeToString() response = verbose_post('https://android.clients.google.com/checkin', data=data, headers=headers) proto_response = AndroidCheckinResponse() proto_response.ParseFromString(response.content) self.device_checkin_consistency_token = proto_response.deviceCheckinConsistencyToken android_id = proto_response.androidId security_token = proto_response.securityToken # TODO We may not need the second checkin second_request = request second_request.id = android_id second_request.securityToken = security_token second_request.accountCookie.append(f'[{self.mail}]') second_request.accountCookie.append(ac2dm_token) second_data = second_request.SerializeToString() verbose_post('https://android.clients.google.com/checkin', data=second_data, headers=headers) LOGGER.info(f'Successfully checked in, got Android id {android_id}') return android_id
def _authorize(self, auth_string): """Authorizes a login to Google Play. Parameters ---------- auth_string : str The authorization string as issued by Google Play. """ params, headers = self._login_parameters(auth_string) params['app'] = headers['app'] = 'com.android.vending' params['service'] = 'androidmarket' response = verbose_post('https://android.clients.google.com/auth', data=params, headers=headers) master_token = get_token(response, 'token') params['Token'] = master_token params['check_email'] = '1' params['token_request_options'] = 'CAA4AQ==' params['system_partition'] = '1' params['_opt_is_called_from_account_manager'] = '1' params.pop('Email') params.pop('EncryptedPasswd') response = verbose_post('https://android.clients.google.com/auth', data=params, headers=headers) second_token = get_token(response, 'auth') self.auth_token = second_token LOGGER.info('Successfully authorized the device with Google Play')
def _retrieve_ac2dm_token(self, auth_string): """Retrieves an ac2dm token, part of the login procedure. Should never be called on its own. Parameters ---------- auth_string : str An authentication string derived from Google Play credentials. Returns ------- str The retrieved token. """ LOGGER.info(f'Retrieving ac2dm-Token with authentication string {auth_string}') login_parameters, login_headers = self._login_parameters(auth_string) login_headers['app'] = 'com.google.android.gms' response = verbose_post('https://android.clients.google.com/auth', data=login_parameters, headers=login_headers) ac2dm_token = get_token(response, 'auth') return ac2dm_token