├── __init__.py ├── googleplay_api ├── __init__.py ├── config.example.ini ├── config.py ├── crypt_utils.py ├── googleplay.py └── googleplay.proto ├── requirements.txt ├── .gitignore ├── README.md └── main.py /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /googleplay_api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | clint==0.5.1 2 | requests==2.22.0 3 | protobuf==3.4.0 4 | pycryptodome==3.8.2 -------------------------------------------------------------------------------- /googleplay_api/config.example.ini: -------------------------------------------------------------------------------- 1 | [Main] 2 | # http proxy used to contact play store 3 | # e.g. 4 | # http_proxy = http://127.0.0.1:3128 5 | # https_proxy = https://144.32.32.32:8080 6 | http_proxy = 7 | https_proxy = 8 | 9 | # can be en_US, fr_FR, ... 10 | lang = en_GB 11 | 12 | # Google Service Framework ID (GSF ID) 13 | android_id = 1234567890ABCDEF 14 | 15 | # device emulated, linked to android_id. User defined string, useful to remember what device refers to android_id 16 | device = Google Pixel (api 25) 17 | 18 | # Note that, although less elegant, using login and password instead of auth_token is more resilient (auth_token kind 19 | # of expire quickly) 20 | # username@gmail.com 21 | google_login = myemail@gmail.com 22 | google_password = mypassword1234 23 | 24 | # subAuth token, alternative to username-password 25 | auth_token : 26 | -------------------------------------------------------------------------------- /googleplay_api/config.py: -------------------------------------------------------------------------------- 1 | import configparser 2 | import os 3 | 4 | __location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__))) 5 | 6 | CONFIG_FILE = os.path.join(__location__, "config.ini") 7 | MAIN_SEC = "Main" 8 | 9 | c = configparser.ConfigParser() 10 | c.read(CONFIG_FILE) 11 | 12 | 13 | def config_section_map(section): 14 | """ 15 | :param section: name of the section in the .ini file (i.e. Main) 16 | :return: returns a dictionary that contains all the configuration settings in the 17 | given section 18 | :rtype: dict 19 | """ 20 | dict1 = {} 21 | options = c.options(section) 22 | for option in options: 23 | try: 24 | dict1[option] = c.get(section, option) 25 | except configparser.NoSectionError or configparser.NoOptionError: 26 | dict1[option] = None 27 | return dict1 28 | 29 | 30 | def get_option(opt): 31 | return config_section_map(MAIN_SEC)[opt] 32 | 33 | 34 | # force the user to edit this file 35 | if any([each is None for each in 36 | [get_option("android_id"), get_option("google_login"), get_option("google_password")]]): 37 | raise Exception("config.ini not updated") 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Custom 2 | googleplay_api/config.ini 3 | .idea/ 4 | 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | env/ 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | .hypothesis/ 51 | 52 | # Translations 53 | *.mo 54 | *.pot 55 | 56 | # Django stuff: 57 | *.log 58 | local_settings.py 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # Jupyter Notebook 74 | .ipynb_checkpoints 75 | 76 | # pyenv 77 | .python-version 78 | 79 | # celery beat schedule file 80 | celerybeat-schedule 81 | 82 | # SageMath parsed files 83 | *.sage.py 84 | 85 | # dotenv 86 | .env 87 | 88 | # virtualenv 89 | .venv 90 | venv/ 91 | ENV/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Google Play Unofficial Python 3 API Library 2 | 3 | Based on the original googleplay-api project by Emilien Girault: 4 | https://github.com/egirault/googleplay-api 5 | 6 | An unofficial Python API that let you search, browse and download Android apps from Google Play (formerly Android Market). 7 | 8 | ## Disclaimer 9 | **This is not an official API. I am not afiliated with Google in any way, and am not responsible of any damage that could be done with it. Use it at your own risk.** 10 | 11 | ## Dependencies 12 | * [Python 3.5+](http://www.python.org) 13 | * [Protocol Buffers](http://code.google.com/p/protobuf/) 14 | * [Requests](http://docs.python-requests.org/en/master/) 15 | * [Clint (used for command-line progress bar)](https://github.com/kennethreitz/clint) 16 | 17 | You can install the required dependencies with _pip_ (a _requirements.txt_ file is provided for this purpose). 18 | 19 | ## Requirements 20 | You must create a `config.ini` file before using the provided scripts (you can copy `config.example.ini` and modify the required fields). First, you need to provide your phone's `androidID`: 21 | 22 | # Google Service Framework ID (GSF ID) 23 | android_id = 1234567890ABCDEF 24 | 25 | To get your `androidID`, use `*#*#8255#*#*` on your phone to start *Gtalk Monitor*. The hex string listed after `aid` is your `androidID`. 26 | 27 | In order to authenticate to Google Play, you also need to provide either your Google login and password, or a valid Google Play Store token. 28 | 29 | ## Features 30 | 31 | - Get package details (description, permissions, price...) 32 | - Search for apps 33 | - List apps in (sub)categories 34 | - List apps similar to another app 35 | - List categories and subcategories 36 | - List reviews for a certain app 37 | - Download apks 38 | - Automatically throttle requests frequency to avoid server errors (Too Many Requests) 39 | - Results paging 40 | 41 | ## Usage Examples 42 | >>> googleplay_api.googleplay import GooglePlayAPI 43 | >>> play_store = GooglePlayAPI(throttle=True) 44 | >>> play_store.login() 45 | >>> play_store.details("com.android.chrome") 46 | or 47 | >>> play_store.search("calculator app", maxResults=100) 48 | or 49 | >>> play_store.list("GAME_ARCADE", "apps_topselling_free") 50 | or 51 | >>> play_store.listSimilar("com.android.chrome") 52 | or 53 | >>> play_store.bulkDetails(["com.android.chrome", "org.mozilla.firefo"]) 54 | or 55 | >>> play_store.getPages(play_store.search("calculator app")) 56 | or 57 | >>> play_store.browse("GAME_ARCADE") 58 | or 59 | >>> play_store.reviews("com.android.chrome") 60 | or 61 | >>> play_store.download("com.android.chrome", 307112552) 62 | 63 | Check docstrings for more information. 64 | 65 | ## License 66 | 67 | This project is released under the BSD license. 68 | 69 | -------------------------------------------------------------------------------- /googleplay_api/crypt_utils.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import hashlib 3 | import binascii 4 | 5 | from Crypto.PublicKey import RSA 6 | from Crypto.Cipher import PKCS1_OAEP 7 | 8 | # The key is the Play Services public key distributed with Google Play Services. 9 | # This one is from version 7.3.29. 10 | B64_KEY_7_3_29 = (b"AAAAgMom/1a/v0lblO2Ubrt60J2gcuXSljGFQXgcyZWveWLEwo6prwgi3" 11 | b"iJIZdodyhKZQrNWp5nKJ3srRXcUW+F1BD3baEVGcmEgqaLZUNBjm057pK" 12 | b"RI16kB0YppeGx5qIQ5QjKzsR8ETQbKLNWgRY0QRNVz34kMJR3P/LgHax/" 13 | b"6rmf5AAAAAwEAAQ==") 14 | 15 | 16 | def bytes_to_long(s): 17 | return int.from_bytes(s, "big") 18 | 19 | 20 | def long_to_bytes(lnum, padmultiple=1): 21 | """Packs the lnum (which must be convertable to a long) into a 22 | byte string 0 padded to a multiple of padmultiple bytes in size. 0 23 | means no padding whatsoever, so that packing 0 result in an empty 24 | string. The resulting byte string is the big-endian two's 25 | complement representation of the passed in long.""" 26 | 27 | # source: http://stackoverflow.com/a/14527004/1231454 28 | 29 | if lnum == 0: 30 | return b'\0' * padmultiple 31 | elif lnum < 0: 32 | raise ValueError("Can only convert non-negative numbers.") 33 | s = hex(lnum)[2:] 34 | s = s.rstrip('L') 35 | if len(s) & 1: 36 | s = '0' + s 37 | s = binascii.unhexlify(s) 38 | if (padmultiple != 1) and (padmultiple != 0): 39 | filled_so_far = len(s) % padmultiple 40 | if filled_so_far != 0: 41 | s = b'\0' * (padmultiple - filled_so_far) + s 42 | return s 43 | 44 | 45 | def key_from_b64(b64_key): 46 | binaryKey = base64.b64decode(b64_key) 47 | 48 | i = bytes_to_long(binaryKey[:4]) 49 | mod = bytes_to_long(binaryKey[4:4 + i]) 50 | 51 | j = bytes_to_long(binaryKey[i + 4:i + 4 + 4]) 52 | exponent = bytes_to_long(binaryKey[i + 8:i + 8 + j]) 53 | 54 | key = RSA.construct((mod, exponent)) 55 | 56 | return key 57 | 58 | 59 | def key_to_struct(key): 60 | mod = long_to_bytes(key.n) 61 | exponent = long_to_bytes(key.e) 62 | 63 | return b'\x00\x00\x00\x80' + mod + b'\x00\x00\x00\x03' + exponent 64 | 65 | 66 | def parse_auth_response(text): 67 | response_data = {} 68 | for line in text.split('\n'): 69 | if not line: 70 | continue 71 | 72 | key, _, val = line.partition('=') 73 | response_data[key] = val 74 | 75 | return response_data 76 | 77 | 78 | def encrypt_login(email, password, b64_key=B64_KEY_7_3_29): 79 | signature = bytearray(b'\x00') 80 | 81 | key = key_from_b64(b64_key) 82 | 83 | struct = key_to_struct(key) 84 | signature.extend(hashlib.sha1(struct).digest()[:4]) 85 | 86 | cipher = PKCS1_OAEP.new(key) 87 | encrypted_login = cipher.encrypt((email + u'\x00' + password).encode('utf-8')) 88 | 89 | signature.extend(encrypted_login) 90 | 91 | return base64.urlsafe_b64encode(signature) 92 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import logging 3 | import os 4 | 5 | import requests 6 | 7 | import googleplay_api.config as play_conf 8 | from googleplay_api.googleplay import GooglePlayAPI 9 | 10 | emulated_device = play_conf.get_option("device") 11 | play_store = None 12 | 13 | 14 | def get_message(path): 15 | print(play_store.freeRequest(path)) 16 | 17 | 18 | def get_details(package): 19 | details, pages = play_store.details(package, True) 20 | print(details) 21 | for page in pages.values(): 22 | print(page) 23 | 24 | 25 | def get_bulk_details(packages): 26 | print(play_store.bulkDetails(packages, True, True)) 27 | 28 | 29 | def search(query): 30 | print(play_store.getPages(play_store.search(query))) 31 | 32 | 33 | def list_category(cat, subcat): 34 | if not cat and not subcat: 35 | print(play_store.browse()) 36 | else: 37 | print(play_store.getPages(play_store.list(cat=cat, ctr=subcat))) 38 | 39 | 40 | def get_similar(package): 41 | print(play_store.getPages(play_store.listSimilar(package))) 42 | 43 | 44 | def get_latest_versioncode(package): 45 | """ 46 | Gets the version code of latest available apk 47 | 48 | :param package: app's package, e.g. com.android.chrome 49 | :return: version code of latest available apk 50 | """ 51 | # noinspection PyPep8Naming 52 | detailsResponse = play_store.details(package) 53 | return detailsResponse.docV2.details.appDetails.versionCode 54 | 55 | 56 | def download_apk(package, version_code, output_path): 57 | """ 58 | :param package: app's package, e.g. com.android.chrome 59 | :param version_code: which version of the app you want to download 60 | :param output_path: where to save the apk file 61 | """ 62 | if not version_code: 63 | version_code = get_latest_versioncode(package) 64 | print("Latest Version Code: {0}".format(version_code)) 65 | data = play_store.download(package, version_code, progressBar=False) 66 | if not data: 67 | print("Error downloading apk.") 68 | return 69 | # warning: we must emulate an ATOMIC write to avoid unfinished files. 70 | # To do so, we use the os.rename() function that should always be atomic under 71 | # certain conditions (https://linux.die.net/man/2/rename) 72 | with open(output_path + ".temp", "wb") as f: 73 | f.write(data) 74 | os.rename(output_path + ".temp", output_path) 75 | 76 | 77 | def main(): 78 | logging.basicConfig(level=logging.INFO) 79 | 80 | parser = argparse.ArgumentParser( 81 | description='Unofficial PlayStore python interface', add_help=True 82 | ) 83 | parser.add_argument('--request', action="store", dest='request_path', 84 | help='Do a generic request, useful for deugging') 85 | parser.add_argument('--details', action="store", dest='package_to_detail', help='Shows various details for the ' 86 | 'given package') 87 | parser.add_argument('--search', action="store", dest='query', help='Search the playstore') 88 | parser.add_argument('--similar', action="store", dest='package_similar', help='Shows various packages similar ' 89 | 'to the given package') 90 | parser.add_argument('--bulk-details', action="store", dest='packages_to_detail', nargs='+', type=str, 91 | help='Shows details for a list of packages') 92 | list_group = parser.add_argument_group() 93 | list_group.add_argument('--list', action="store_true", dest='list', 94 | help='List the categories avilable on playstore. If --category is specified, lists the ' 95 | 'subcategories of the specified category. If --subcategory is also specified, lists ' 96 | 'the app in the subcategory.') 97 | list_group.add_argument('--category', action="store", dest='cat', 98 | help='Specify a certain category (e.g. GAME_ARCADE)') 99 | list_group.add_argument('--subcategory', action="store", dest='subcat', 100 | help='Specify a certain subcategory (e.g. apps_topselling_free)') 101 | group = parser.add_argument_group() 102 | group.add_argument('--download', action="store", dest='package_to_download', help='Download the apk with given ' 103 | 'package name') 104 | group.add_argument('-o', action="store", dest='output_folder', help='(optional) Where to ' 105 | 'save the downloaded apk') 106 | group.add_argument('--version', action="store", dest='version_code', help='(optional) Version Code of the apk' 107 | 'to download (default: latest)') 108 | parser.add_argument('--remote-token', action="store", dest='token_url', help='If the authentication token should be' 109 | ' retrieved from a remote server') 110 | group_proxy = parser.add_argument_group() 111 | group_proxy.add_argument('--http-proxy', action="store", dest='http_proxy', help='http proxy, ONLY used for' 112 | 'Play Store requests!') 113 | group_proxy.add_argument('--https-proxy', action="store", dest='https_proxy', help='https proxy, ONLY used for' 114 | 'Play Store requests!') 115 | 116 | results = parser.parse_args() 117 | 118 | proxies = None 119 | if results.http_proxy: 120 | if proxies is None: 121 | proxies = {} 122 | proxies["http"] = results.http_proxy 123 | if results.https_proxy: 124 | if proxies is None: 125 | proxies = {} 126 | proxies["https"] = results.https_proxy 127 | global play_store 128 | play_store = GooglePlayAPI(throttle=True, proxies=proxies) 129 | token = None 130 | if results.token_url: 131 | response = requests.get(results.token_url) 132 | token = response.text 133 | print("Using auth token: {0}".format(token)) 134 | play_store.login(authSubToken=token) 135 | 136 | if results.request_path: 137 | get_message(results.request_path) 138 | return 139 | 140 | if results.package_to_detail: 141 | get_details(results.package_to_detail) 142 | return 143 | 144 | if results.query: 145 | search(results.query) 146 | return 147 | 148 | if results.packages_to_detail: 149 | get_bulk_details(results.packages_to_detail) 150 | return 151 | 152 | if results.package_similar: 153 | get_similar(results.package_similar) 154 | return 155 | 156 | if results.list: 157 | list_category(results.cat, results.subcat) 158 | return 159 | 160 | if results.package_to_download: 161 | package = results.package_to_download 162 | version = results.version_code 163 | output_folder = results.output_folder 164 | if output_folder: 165 | os.path.join(output_folder, package + ".apk") 166 | else: 167 | output_folder = package + ".apk" 168 | download_apk(results.package_to_download, version, output_folder) 169 | return 170 | 171 | parser.print_help() 172 | 173 | 174 | if __name__ == '__main__': 175 | main() 176 | -------------------------------------------------------------------------------- /googleplay_api/googleplay.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import logging 3 | import operator 4 | from collections import OrderedDict 5 | from time import sleep 6 | from urllib.parse import urlsplit, parse_qs, urlencode 7 | 8 | import requests 9 | from google.protobuf import descriptor 10 | from google.protobuf import text_format 11 | from google.protobuf.internal.containers import RepeatedCompositeFieldContainer 12 | from google.protobuf.message import Message 13 | 14 | from . import config 15 | from . import googleplay_pb2 16 | from . import crypt_utils 17 | 18 | MIN_THROTTLE_TIME = 0.05 19 | MAX_PREFETCH_ELEMENTS = 200 20 | 21 | # should be always True, but we leave this here for testing purpose 22 | ssl_verify = True 23 | if not ssl_verify: 24 | # noinspection PyUnresolvedReferences 25 | import requests.packages.urllib3 as urllib3 26 | 27 | urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) 28 | logging.warning("Warning: you are making unverified HTTPS requests!!!") 29 | 30 | 31 | class LoginError(Exception): 32 | def __init__(self, value): 33 | self.value = value 34 | 35 | def __str__(self): 36 | return repr(self.value) 37 | 38 | 39 | class RequestError(Exception): 40 | def __init__(self, value, http_status): 41 | self.value = value 42 | self.http_status = http_status 43 | 44 | def __str__(self): 45 | return repr(self.value) 46 | 47 | 48 | class DownloadError(Exception): 49 | def __init__(self, value): 50 | self.value = value 51 | 52 | def __str__(self): 53 | return repr(self.value) 54 | 55 | 56 | # noinspection PyPep8Naming 57 | class GooglePlayAPI(object): 58 | """ 59 | Google Play Unofficial API Class 60 | 61 | Usual APIs methods are login(), search(), details(), bulkDetails(), 62 | download(), browse(), reviews() listSimilar() and list(). 63 | 64 | toStr() can be used to pretty print the result (protobuf object) of the 65 | previous methods. 66 | 67 | toDict() converts the result into a dict, for easier introspection.""" 68 | 69 | SERVICE = "androidmarket" 70 | 71 | URL_LOGIN = "https://android.clients.google.com/auth" 72 | ACCOUNT_TYPE_GOOGLE = "GOOGLE" 73 | ACCOUNT_TYPE_HOSTED = "HOSTED" 74 | ACCOUNT_TYPE_HOSTED_OR_GOOGLE = "HOSTED_OR_GOOGLE" 75 | 76 | def __init__(self, androidId=None, lang=None, debug=False, throttle=False, errorRetries=3, errorRetryTimeout=5, 77 | proxies=None): 78 | """ 79 | :param androidId: you must use a device-associated androidId value, 80 | decides the kind of result that can be retrieved 81 | :param lang: language code to determine play store language, e.g. en_GB or it_IT or en_US 82 | :param debug: if True, prints debug info 83 | :param throttle: if True, in case of 429 errors (Too Many Requests), uses exponential backoff to 84 | increase delay and retry request until success. If False, ignores 429 errors 85 | :param errorRetries: how many times retry to make a request that failed (except 429 HTTP status) 86 | :param errorRetryTimeout: how many second sleep after failing a request (except 429 HTTP status) 87 | :param proxies: a dictionary containing (protocol, address) key-value pairs, e.g. 88 | {"http":"http://123.0.123.100:8080", "https": "https://231.1.2.34:3123"} 89 | If None, no proxy will be used 90 | """ 91 | self.preFetch = OrderedDict() 92 | if androidId is None: 93 | androidId = config.get_option("android_id") 94 | if lang is None: 95 | lang = config.get_option("lang") 96 | if throttle: 97 | self.throttleTime = MIN_THROTTLE_TIME 98 | if proxies is None: 99 | proxies = {} 100 | if config.get_option("http_proxy"): 101 | proxies["http"] = config.get_option("http_proxy") 102 | if config.get_option("https_proxy"): 103 | proxies["https"] = config.get_option("https_proxy") 104 | 105 | self.androidId = androidId 106 | self.lang = lang 107 | self.throttle = throttle 108 | self.errorRetries = errorRetries 109 | self.errorRetryTimeout = errorRetryTimeout 110 | self.downloadUserAgent = "AndroidDownloadManager/7.1 (Linux; U; Android 7.1; Pixel Build/NZZ99Z)" 111 | self.defaultAgentvername = "7.0.12.H-all [0]" # updating these two values could broke the application 112 | self.defaultAgentvercode = "80701200" # versionCode should be the version code of the Play Store app 113 | self.debug = debug 114 | self.proxies = proxies 115 | self.authSubToken = None 116 | self.email = None 117 | self.password = None 118 | 119 | def toDict(self, protoObj): 120 | """ 121 | Converts the (protobuf) result from an API call into a dict, for 122 | easier introspection. 123 | 124 | :param protoObj: protobuf object 125 | :return: a dictionary or a list of dictionaries, depending on the protobuf object structure 126 | :rtype: Union[None, list, dict] 127 | """ 128 | iterable = False 129 | if isinstance(protoObj, RepeatedCompositeFieldContainer): 130 | iterable = True 131 | else: 132 | protoObj = [protoObj] 133 | retlist = [] 134 | 135 | for po in protoObj: 136 | msg = dict() 137 | for fielddesc, value in po.ListFields(): 138 | # print value, type(value), getattr(value, "__iter__", False) 139 | if fielddesc.type == descriptor.FieldDescriptor.TYPE_GROUP or \ 140 | isinstance(value, RepeatedCompositeFieldContainer) or \ 141 | isinstance(value, Message): 142 | # noinspection PyTypeChecker 143 | msg[fielddesc.name] = self.toDict(value) 144 | else: 145 | msg[fielddesc.name] = value 146 | retlist.append(msg) 147 | if not iterable: 148 | if len(retlist) > 0: 149 | return retlist[0] 150 | else: 151 | return None 152 | return retlist 153 | 154 | # noinspection PyMethodMayBeStatic 155 | def toStr(self, protoObj): 156 | """ 157 | Used for pretty printing a result from the API. 158 | 159 | :param protoObj: protobuf object 160 | :return: a string representing the protobuf object 161 | :rtype: str 162 | """ 163 | return text_format.MessageToString(protoObj) 164 | 165 | def _try_register_preFetch(self, protoObj): 166 | fields = [i.name for (i, _) in protoObj.ListFields()] 167 | if "preFetch" in fields: 168 | for p in protoObj.preFetch: 169 | self.preFetch[p.url] = p.response 170 | if 0 < MAX_PREFETCH_ELEMENTS < len(self.preFetch.keys()): 171 | # To avoid memory leaks, remove elements from dict when reaching limit. 172 | # Set MAX_PREFETCH_ELEMENTS to -1 to disable it 173 | difference = len(self.preFetch.keys()) - MAX_PREFETCH_ELEMENTS 174 | for i in range(difference): 175 | self.preFetch.popitem(False) # False => FIFO 176 | 177 | def setAuthSubToken(self, authSubToken): 178 | self.authSubToken = authSubToken 179 | # put your auth token in config.ini to avoid multiple login requests 180 | if self.debug: 181 | print("authSubToken: " + authSubToken) 182 | 183 | def get_second_round_token(self, params, headers, token): 184 | new_params = params.copy() 185 | new_params["Token"] = token 186 | new_params["androidId"] = self.androidId 187 | new_params["check_email"] = "1" 188 | new_params["token_request_options"] = "CAA4AQ==" 189 | new_params["system_partition"] = "1" 190 | new_params["_opt_is_called_from_account_manager"] = "1" 191 | del new_params["Email"] 192 | del new_params["EncryptedPasswd"] 193 | response = requests.post(self.URL_LOGIN, data=new_params, 194 | headers=headers, verify=ssl_verify, proxies=self.proxies) 195 | data = response.text.split() 196 | response_params = {} 197 | for d in data: 198 | if "=" not in d: 199 | continue 200 | k, v = d.split("=")[0:2] 201 | response_params[k.strip().lower()] = v.strip() 202 | if "auth" in response_params: 203 | return response_params["auth"] 204 | elif "error" in response_params: 205 | logging.warning("server says: " + response_params["error"]) 206 | else: 207 | logging.warning("Auth token not found in second round.") 208 | return None 209 | 210 | def login(self, email=None, password=None, authSubToken=None): 211 | """ 212 | Login to your Google Account. You must provide either: 213 | - an email and password 214 | - a valid Google authSubToken 215 | 216 | :param email: your gmail email (e.g. example@gmail.com) 217 | :param password: password of tha gmail email 218 | :param authSubToken: Play Store authentication token, i.e. something like this: 219 | AQVSHqIoXFwQM4oK57PKp7x3kzo17tk1cA-kAd77kpsPwoeyNNzDiQtQjJPgQuda-D25WA. 220 | """ 221 | if email is None and password is None and authSubToken is None: 222 | # no parameter provided, loading from conf file 223 | email = config.get_option("google_login") 224 | password = config.get_option("google_password") 225 | authSubToken = config.get_option("auth_token") 226 | if authSubToken: 227 | self.setAuthSubToken(authSubToken) 228 | else: 229 | self.email = email 230 | self.password = password 231 | if not email or not password: 232 | raise LoginError("You should provide at least authSubToken or (email and password)") 233 | encrypted_password = crypt_utils.encrypt_login(email, password) 234 | params = {"Email": email, 235 | "EncryptedPasswd": encrypted_password, 236 | "service": self.SERVICE, 237 | "accountType": self.ACCOUNT_TYPE_HOSTED_OR_GOOGLE, 238 | "has_permission": "1", 239 | "source": "android", 240 | "androidId": self.androidId, 241 | "app": "com.android.vending", 242 | # "client_sig": self.client_sig, 243 | "device_country": "en", 244 | "operatorCountry": "en", 245 | "lang": "en", 246 | "sdk_version": "19"} 247 | headers = { 248 | "Accept-Encoding": "", 249 | "app": "com.android.vending", 250 | } 251 | response = requests.post(self.URL_LOGIN, data=params, 252 | headers=headers, verify=ssl_verify, proxies=self.proxies) 253 | data = response.text.split() 254 | response_params = {} 255 | for d in data: 256 | if "=" not in d: 257 | continue 258 | k, v = d.split("=")[0:2] 259 | response_params[k.strip().lower()] = v.strip() 260 | if "token" in response_params: 261 | logging.info("Token found in response params. Trying to request a \"second round\" token.") 262 | second_round_token = self.get_second_round_token(params, headers, response_params["token"]) 263 | if second_round_token is not None: 264 | self.setAuthSubToken(second_round_token) 265 | return 266 | if "auth" in response_params: 267 | self.setAuthSubToken(response_params["auth"]) 268 | elif "error" in response_params: 269 | raise LoginError("server says: " + response_params["error"]) 270 | else: 271 | raise LoginError("Auth token not found.") 272 | 273 | def executeRequestApi2(self, path, sdk=25, agentvername=None, agentvercode=None, devicename="sailfish", 274 | datapost=None, post_content_type="application/x-www-form-urlencoded; charset=UTF-8"): 275 | """ 276 | Builds and submits a valid request to the Google Play Store 277 | 278 | :param path: url path, depends on the endpoint that should be contacted 279 | e.g. details?doc=com.android.chrome 280 | :param sdk: from which sdk version should the request appear to come from, e.g. 25 281 | :param agentvername: version name of the user agent, i.e. of the market app that we are spoofing; 282 | used to build the User Agent 283 | :param agentvercode: version code of the user agent, i.e. of the market app that we are spoofing; 284 | used to build the User Agent 285 | :param devicename: from which device should the request appear to come from; used to build the User Agent 286 | :param datapost: payload of the post request, if any 287 | :param post_content_type: content_type field of the post request 288 | :return: a protobuf object with the server response 289 | :rtype: ResponseWrapper 290 | """ 291 | if not agentvername: 292 | agentvername = self.defaultAgentvername 293 | if not agentvercode: 294 | agentvercode = self.defaultAgentvercode 295 | user_agent = "Android-Finsky/" + agentvername + " (api=3,versionCode=" + agentvercode + ",sdk=" + \ 296 | str(sdk) + ",device=" + devicename + ",hardware=" + devicename + ",product=" + \ 297 | devicename + ",build=NZZ99Z:user)" 298 | if datapost is None and path in self.preFetch: 299 | data = self.preFetch[path] 300 | else: 301 | url = "https://android.clients.google.com/fdfe/{0}".format(path) 302 | response = None 303 | errorRetries = self.errorRetries 304 | retry = True 305 | while retry: 306 | if self.throttle: 307 | sleep(self.throttleTime) 308 | 309 | headers = {"Accept-Language": self.lang, 310 | "Authorization": "GoogleLogin auth={0}".format(self.authSubToken), 311 | "X-DFE-Enabled-Experiments": "cl:billing.select_add_instrument_by_default", 312 | "X-DFE-Unsupported-Experiments": "nocache:billing.use_charging_poller," 313 | "market_emails,buyer_currency,prod_baseline," 314 | "checkin.set_asset_paid_app_field,shekel_test," 315 | "content_ratings,buyer_currency_in_app," 316 | "nocache:encrypted_apk,recent_changes", 317 | "X-DFE-Device-Id": self.androidId, 318 | "X-DFE-Client-Id": "am-android-google", 319 | # "X-DFE-Logging-Id": self.loggingId2, # Deprecated? 320 | "User-Agent": user_agent, 321 | "X-DFE-SmallestScreenWidthDp": "320", 322 | "X-DFE-Filter-Level": "3", 323 | # "X-DFE-No-Prefetch": 'true', # avoid prefetch 324 | "Accept-Encoding": "gzip, deflate", 325 | "Host": "android.clients.google.com"} 326 | 327 | if datapost is not None: 328 | headers["Content-Type"] = post_content_type 329 | if datapost is not None: 330 | response = requests.post(url, data=datapost, headers=headers, verify=ssl_verify, 331 | proxies=self.proxies) 332 | else: 333 | response = requests.get(url, headers=headers, verify=ssl_verify, proxies=self.proxies) 334 | response_code = response.status_code 335 | if int(response_code) == 429 and self.throttle: 336 | # there seems to be no "retry" header, so we have to resort to exponential backoff 337 | self.throttleTime *= 2 338 | logging.warning("Too many request reached. " 339 | "Throttling connection (sleep {0})...".format(self.throttleTime)) 340 | elif int(response_code) == 401 and errorRetries > 0 and self.password is not None and self.email is not None: 341 | logging.warning("Received 401; trying to obtain a new subAuth token from credentials") 342 | self.login(self.email, self.password) 343 | errorRetries -= 1 344 | elif int(response_code) != 200: 345 | logging.error("Response code: {0} triggered by: {1} " 346 | "with datapost: {2}".format(response_code, url, str(datapost))) 347 | logging.error(response.content) 348 | if errorRetries <= 0: 349 | raise RequestError("Error during http request: {0}".format(response_code), response_code) 350 | else: 351 | sleep(max(self.throttleTime, self.errorRetryTimeout)) 352 | errorRetries -= 1 353 | else: 354 | retry = False 355 | if self.throttle and self.throttleTime > MIN_THROTTLE_TIME: 356 | retry = False 357 | self.throttleTime /= 2 358 | 359 | data = response.content 360 | ''' 361 | data = StringIO.StringIO(data) 362 | gzipper = gzip.GzipFile(fileobj=data) 363 | data = gzipper.read() 364 | ''' 365 | message = googleplay_pb2.ResponseWrapper.FromString(data) 366 | self._try_register_preFetch(message) 367 | 368 | # Debug 369 | # print(text_format.MessageToString(message)) 370 | if not str(message) or not str(message.payload): 371 | logging.warning(data) 372 | return message 373 | 374 | ##################################### 375 | # Google Play API Methods 376 | ##################################### 377 | 378 | def freeRequest(self, path, datapost=None): 379 | """ 380 | Do a generic request, useful for debugging 381 | 382 | :param path: request path 383 | :param datapost: post data (if any) 384 | :return: Message 385 | """ 386 | return self.executeRequestApi2(path, datapost=datapost) 387 | 388 | def details(self, packageName, getPrefetchPages=False): 389 | """ 390 | Get app details from a package name. 391 | 392 | :param packageName: the app unique ID e.g. 'com.android.chrome' or 'org.mozilla.firefox' 393 | :param getPrefetchPages: if True, also returns the pre-fetched pages that were sent by the server. 394 | These pages will be already in cache after the details request, so requesting 395 | these will not result in a remote call. 396 | :return: details for packageName 397 | :rtype: Union[DetailsResponse, (DetailsResponse, dict)] 398 | """ 399 | path = "details?doc={0}".format(packageName) 400 | message = self.executeRequestApi2(path) 401 | if not getPrefetchPages: 402 | return message.payload.detailsResponse 403 | else: 404 | prefetchedPages = {} 405 | for prefetch in message.preFetch: 406 | prefetchMessage = self.executeRequestApi2(prefetch.url) 407 | listResponse = prefetchMessage.payload.listResponse 408 | for doc in listResponse.doc: 409 | if doc.backendDocid == ("similar_apps_" + packageName): 410 | if prefetchedPages.get("similar", None) is not None: 411 | logging.error("Similar page already prefetched for package {0}!".format(packageName)) 412 | else: 413 | prefetchedPages["similar"] = listResponse 414 | elif doc.backendDocid == ("pre_install_users_also_installed_" + packageName): 415 | if prefetchedPages.get("preInstall", None) is not None: 416 | logging.error("Pre-install page already prefetched for package {0}!".format(packageName)) 417 | else: 418 | prefetchedPages["preInstall"] = listResponse 419 | elif doc.backendDocid == ("post_install_users_also_installed_" + packageName): 420 | if prefetchedPages.get("postInstall", None) is not None: 421 | logging.error("Post-install page already prefetched for package {0}!".format(packageName)) 422 | else: 423 | prefetchedPages["postInstall"] = listResponse 424 | else: 425 | logging.error("Unknown prefetch: {1} for package {0}!".format(packageName, doc.backendDocid)) 426 | 427 | return message.payload.detailsResponse, prefetchedPages 428 | 429 | def search(self, query, maxResults=None, offset=None): 430 | """ 431 | Search for apps. 432 | 433 | :param query: Query to submit, e.g. 'best riddles game' 434 | :param maxResults: max result per page; WARNING despite being seen as parameter during play store 435 | Reverse Engineering, it seems to be ignored by the server (fixed to 20); 436 | use getPages for more results 437 | :param offset: skip the first offset results 438 | :return: search results for the submitted query 439 | :rtype: SearchResponse 440 | 441 | """ 442 | path = "search?c=3&q={0}".format(requests.utils.quote(query)) # TODO handle categories 443 | if maxResults is not None: 444 | path += "&n={0}".format(int(maxResults)) 445 | if offset is not None: 446 | path += "&o={0}".format(int(offset)) 447 | 448 | message = self.executeRequestApi2(path) 449 | return message.payload.searchResponse 450 | 451 | def list(self, cat=None, ctr=None, maxResults=None, offset=None): 452 | """ 453 | List apps for a given category-subcategory pair, subcategories if only category is provided. 454 | 455 | :param cat: category id, e.g. AUTO_AND_VEHICLES or GAME_ARCADE 456 | :param ctr: subcategory id, e.g. apps_topselling_free or apps_movers_shakers 457 | :param maxResults: max number of results, WARNING: seems to be capped at 100, error for higher values; 458 | use getPages for more results 459 | :param offset: skip the first offset results 460 | :return: apps for given category-subcategory OR list of subcategories if only cat provided 461 | :rtype: ListResponse 462 | """ 463 | path = "list?c=3&cat={0}".format(cat) 464 | if ctr is not None: 465 | path += "&ctr={0}".format(ctr) 466 | if maxResults is not None: 467 | path += "&n={0}".format(int(maxResults)) 468 | if offset is not None: 469 | path += "&o={0}".format(int(offset)) 470 | message = self.executeRequestApi2(path) 471 | return message.payload.listResponse 472 | 473 | def listSimilar(self, packageName, maxResults=None, offset=None): 474 | """ 475 | List apps similar to a given package. 476 | 477 | :param packageName: the app unique ID e.g. 'com.android.chrome' or 'org.mozilla.firefox' 478 | :param maxResults: max number of results, WARNING: seems to be capped at 100, error for higher values; 479 | use getPages for more results 480 | :param offset: skip the first offset results 481 | :return: apps similar to the app identified by packageName 482 | :rtype: ListResponse 483 | """ 484 | # Check this url for further analysis 485 | # browseV2?bt=5&c=3&doc=com.android.chrome&rt=1 486 | path = "rec?c=3&rt=1&doc={0}".format(packageName) 487 | if maxResults is not None: 488 | path += "&n={0}".format(int(maxResults)) 489 | if offset is not None: 490 | path += "&o={0}".format(int(offset)) 491 | 492 | message = self.executeRequestApi2(path) 493 | return message.payload.listResponse 494 | 495 | def bulkDetails(self, packageNames, includeChildDocs=False, includeDetails=False): 496 | """ 497 | Get several apps details from a list of package names. 498 | This is much more efficient than calling N times details() since it 499 | requires only one request. 500 | 501 | :param packageNames: a list of app unique ID e.g. ['com.android.chrome', 'org.mozilla.firefox'] 502 | :param includeChildDocs: include child docs if presents 503 | :param includeDetails: include more details, such as html description and so on 504 | :return: details for the packages specified in packageNames 505 | :rtype: BulkDetailsResponse 506 | """ 507 | path = "bulkDetails" 508 | req = googleplay_pb2.BulkDetailsRequest() 509 | req.docid.extend(packageNames) 510 | req.includeChildDocs = includeChildDocs 511 | req.includeDetails = includeDetails 512 | data = req.SerializeToString() 513 | message = self.executeRequestApi2(path, datapost=data, 514 | post_content_type="application/x-protobuf") 515 | return message.payload.bulkDetailsResponse 516 | 517 | def bulkDetailsFromDocs(self, docs, includeChildDocs=False, includeDetails=False): 518 | """ 519 | Utility method to retrieve details from a list of DocV2. Used mainly to retrieve details 520 | during pagination (getPages). 521 | 522 | :param docs: a list of DocV2 documents 523 | :param includeChildDocs: include child docs if presents 524 | :param includeDetails: include more details, such as html description and so on 525 | :return: details for the packages specified in the documents 526 | :rtype: BulkDetailsResponse 527 | """ 528 | packages = [] 529 | for doc in docs: 530 | for child1 in doc.child: 531 | if child1.docType == 45: 532 | for child2 in child1.child: 533 | if child2.docType == 1: 534 | packages.append(child2.docid) 535 | else: 536 | logging.warning("Unknown docType {0}. Maybe it's not an app?".format(child1.docType)) 537 | elif child1.docType == 1: 538 | packages.append(child1.docid) 539 | else: 540 | logging.warning("Unknown docType {0}. Maybe it's not an app?".format(child1.docType)) 541 | bulk_details = self.bulkDetails(packages, includeChildDocs, includeDetails) 542 | return bulk_details 543 | 544 | def getPages(self, response, maxPages=None, alterMaxResults=None, details=False, includeChildDocs=False, 545 | includeDetails=False): 546 | """ 547 | Given a SearchResponse or ListResponse from e.g. listSimilar or search, returns the passed response 548 | merged to other maxPages-1 pages of responses (or less if not enough results are available). 549 | If details is True, returns the details for each app 550 | 551 | :param response: SearchResponse or ListResponse object 552 | :param maxPages: max number of pages to 553 | :param alterMaxResults: if set to a number, it tries to detect pagination parameters and 554 | increment it to the desired amount. Usually ok for values <= 100 555 | :param details: if True, returns the list of app details 556 | :param includeChildDocs: (valid only if details is True) include child docs if presents 557 | :param includeDetails: (valid only if details is True) include more details, such as html description and so on 558 | :return: a list of apps or app details 559 | :rtype: SearchResponse or ListResponse or BulkDetailsResponse 560 | """ 561 | # if response doesn't contain any doc, return directly the response or None if details were requested 562 | if not response.doc: 563 | if details: 564 | return None 565 | else: 566 | return response 567 | if type(response) == googleplay_pb2.SearchResponse: 568 | response_location = "payload.searchResponse" 569 | elif type(response) == googleplay_pb2.ListResponse: 570 | response_location = "payload.listResponse" 571 | else: 572 | logging.error("Unknown response type: {0}; cannot get pages") 573 | return 574 | all_responses = response 575 | all_details = None 576 | if details: 577 | bulk_details = self.bulkDetailsFromDocs(response.doc, includeChildDocs, includeDetails) 578 | all_details = bulk_details 579 | page = 1 580 | next_page = response.doc[-1].containerMetadata.nextPageUrl 581 | while True: 582 | if maxPages and page >= maxPages: 583 | # break if we have reached the imposed limit 584 | break 585 | if not next_page: 586 | # break if there isn't any page left 587 | break 588 | if alterMaxResults and "n=" in next_page: 589 | # parse original string url 590 | url_data = urlsplit(next_page) 591 | # parse original query-string 592 | qs_data = parse_qs(url_data.query) 593 | # manipulate the query-string 594 | qs_data['n'] = [alterMaxResults] 595 | # get the url with modified query-string 596 | # noinspection PyProtectedMember 597 | next_page = url_data._replace(query=urlencode(qs_data, True)).geturl() 598 | 599 | message = self.executeRequestApi2(next_page) 600 | response = operator.attrgetter(response_location)(message) 601 | if not response.doc: 602 | # break if there are no result 603 | break 604 | all_responses.MergeFrom(response) 605 | if details: 606 | bulk_details = self.bulkDetailsFromDocs(response.doc, includeChildDocs, includeDetails) 607 | all_details.MergeFrom(bulk_details) 608 | page += 1 609 | next_page = response.doc[-1].containerMetadata.nextPageUrl 610 | 611 | if details: 612 | return all_details 613 | return all_responses 614 | 615 | def browse(self, cat=None, ctr=None): 616 | """ 617 | Browse categories; cat (category ID) and ctr (subcategory ID) are used as filters. 618 | 619 | :param cat: category id, e.g. AUTO_AND_VEHICLES or GAME_ARCADE 620 | :param ctr: subcategory id, e.g. apps_topselling_free or apps_movers_shakers 621 | :return: list of categories or subcategories, if cat is provided 622 | :rtype: BrowseResponse 623 | """ 624 | path = "browse?c=3" 625 | if cat is not None: 626 | path += "&cat={0}".format(cat) 627 | if ctr is not None: 628 | path += "&ctr={0}".format(ctr) 629 | message = self.executeRequestApi2(path) 630 | return message.payload.browseResponse 631 | 632 | def reviews(self, packageName, filterByDevice=False, sort=2, maxResults=None, offset=None): 633 | """ 634 | Browse reviews. 635 | 636 | :param packageName: app unique ID e.g. 'com.android.chrome' or 'org.mozilla.firefox' 637 | :param filterByDevice: if True, return only reviews for your device 638 | :param sort: sort index 639 | :param maxResults: max number of results, WARNING: seems to be capped at 100, error for higher values; 640 | change offset for more results 641 | :param offset: skip the first offset results 642 | :return: a list of reviews 643 | :rtype: ReviewResponse 644 | """ 645 | path = "rev?doc={0}&sort={1}".format(packageName, sort) 646 | if maxResults is not None: 647 | path += "&n={0}".format(int(maxResults)) 648 | if offset is not None: 649 | path += "&o={0}".format(int(offset)) 650 | if filterByDevice: 651 | path += "&dfil=1" 652 | message = self.executeRequestApi2(path) 653 | return message.payload.reviewResponse 654 | 655 | def purchase(self, packageName, versionCode, offerType=1): 656 | """ 657 | Purchases an app. Can be used with free apps too. 658 | 659 | :param packageName: app unique ID e.g. 'com.android.chrome' or 'org.mozilla.firefox' 660 | :param versionCode: can be grabbed by using the details() method on the given 661 | :param offerType: seems to have no usage, default at 1 662 | :return: purchase response 663 | :rtype: BuyResponse 664 | """ 665 | path = "purchase" 666 | data = "doc={0}&ot={1}&vc={2}".format(packageName, offerType, versionCode) 667 | message = self.executeRequestApi2(path, datapost=data) 668 | return message.payload.buyResponse 669 | 670 | def delivery(self, packageName, versionCode, offerType=1): 671 | """ 672 | Delivers a purchased or free app, used to retrieve download link. 673 | 674 | :param packageName: app unique ID e.g. 'com.android.chrome' or 'org.mozilla.firefox' 675 | :param versionCode: can be grabbed by using the details() method on the given 676 | :param offerType: seems to have no usage, default at 1 677 | :return: delivery response 678 | :rtype: DeliveryResponse 679 | """ 680 | path = "delivery" 681 | path += "?doc={0}&ot={1}&vc={2}".format(packageName, offerType, versionCode) 682 | message = self.executeRequestApi2(path) 683 | return message.payload.deliveryResponse 684 | 685 | def download(self, packageName, versionCode, offerType=1, progressBar=False): 686 | """ 687 | Retrieves an apk. 688 | 689 | :param packageName: app unique ID e.g. 'com.android.chrome' or 'org.mozilla.firefox' 690 | :param versionCode: can be grabbed by using the details() method on the given 691 | :param offerType: seems to have no usage, default at 1 692 | :param progressBar: True if a progressbar should be shown 693 | :return: the apk content 694 | :rtype: Union[None, bytes, str] 695 | """ 696 | # first "purchase" the app, then "deliver" it if it was already purchased 697 | try: 698 | response = self.purchase(packageName, versionCode, offerType) 699 | except RequestError as e: 700 | raise DownloadError(str(e)) 701 | url = response.purchaseStatusResponse.appDeliveryData.downloadUrl 702 | if len(response.purchaseStatusResponse.appDeliveryData.downloadAuthCookie) > 0: 703 | cookie = response.purchaseStatusResponse.appDeliveryData.downloadAuthCookie[0] 704 | else: 705 | try: 706 | response = self.delivery(packageName, versionCode, offerType) 707 | except RequestError as e: 708 | raise DownloadError(str(e)) 709 | url = response.appDeliveryData.downloadUrl 710 | if len(response.appDeliveryData.downloadAuthCookie) > 0: 711 | cookie = response.appDeliveryData.downloadAuthCookie[0] 712 | else: 713 | logging.error(response) 714 | raise DownloadError("Can't find download Authentication Cookie") 715 | 716 | cookies = { 717 | str(cookie.name): str(cookie.value) # python-requests #459 fixes this 718 | } 719 | 720 | headers = { 721 | "User-Agent": self.downloadUserAgent, 722 | "Accept-Encoding": "", 723 | } 724 | 725 | if not progressBar: 726 | response = requests.get(url, headers=headers, cookies=cookies, verify=ssl_verify, proxies=self.proxies) 727 | return response.content 728 | # If progress_bar is asked 729 | from clint.textui import progress 730 | response_content = bytes() 731 | response = requests.get(url, headers=headers, cookies=cookies, verify=ssl_verify, stream=True, 732 | proxies=self.proxies) 733 | total_length = int(response.headers.get('content-length')) 734 | for chunk in progress.bar(response.iter_content(chunk_size=1024), expected_size=(total_length / 1024) + 1): 735 | if chunk: 736 | response_content += chunk 737 | return response_content 738 | -------------------------------------------------------------------------------- /googleplay_api/googleplay.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | message AckNotificationResponse { 4 | } 5 | message AndroidAppDeliveryData { 6 | optional int64 downloadSize = 1; 7 | optional string signature = 2; 8 | optional string downloadUrl = 3; 9 | repeated AppFileMetadata additionalFile = 4; 10 | repeated HttpCookie downloadAuthCookie = 5; 11 | optional bool forwardLocked = 6; 12 | optional int64 refundTimeout = 7; 13 | optional bool serverInitiated = 8; 14 | optional int64 postInstallRefundWindowMillis = 9; 15 | optional bool immediateStartNeeded = 10; 16 | optional AndroidAppPatchData patchData = 11; 17 | optional EncryptionParams encryptionParams = 12; 18 | optional string gzippedDownloadUrl = 13; 19 | optional int64 gzippedDownloadSize = 14; 20 | repeated SplitDeliveryData splitDeliveryData = 15; 21 | optional int32 installLocation = 16; 22 | } 23 | 24 | message SplitDeliveryData { 25 | optional string id = 1; 26 | optional int64 downloadSize = 2; 27 | optional int64 gzippedDownloadSize = 3; 28 | optional string signature = 4; 29 | optional string downloadUrl = 5; 30 | optional string gzippedDownloadUrl = 6; 31 | optional AndroidAppPatchData patchData = 7; 32 | } 33 | 34 | message AndroidAppPatchData { 35 | optional int32 baseVersionCode = 1; 36 | optional string baseSignature = 2; 37 | optional string downloadUrl = 3; 38 | optional int32 patchFormat = 4; 39 | optional int64 maxPatchSize = 5; 40 | } 41 | message AppFileMetadata { 42 | optional int32 fileType = 1; 43 | optional int32 versionCode = 2; 44 | optional int64 size = 3; 45 | optional string downloadUrl = 4; 46 | } 47 | message EncryptionParams { 48 | optional int32 version = 1; 49 | optional string encryptionKey = 2; 50 | optional string hmacKey = 3; 51 | } 52 | message HttpCookie { 53 | optional string name = 1; 54 | optional string value = 2; 55 | } 56 | message Address { 57 | optional string name = 1; 58 | optional string addressLine1 = 2; 59 | optional string addressLine2 = 3; 60 | optional string city = 4; 61 | optional string state = 5; 62 | optional string postalCode = 6; 63 | optional string postalCountry = 7; 64 | optional string dependentLocality = 8; 65 | optional string sortingCode = 9; 66 | optional string languageCode = 10; 67 | optional string phoneNumber = 11; 68 | optional bool deprecatedIsReduced = 12; 69 | optional string firstName = 13; 70 | optional string lastName = 14; 71 | optional string email = 15; 72 | } 73 | message BookAuthor { 74 | optional string name = 1; 75 | optional string deprecatedQuery = 2; 76 | optional Docid docid = 3; 77 | } 78 | message BookDetails { 79 | repeated BookSubject subject = 3; 80 | optional string publisher = 4; 81 | optional string publicationDate = 5; 82 | optional string isbn = 6; 83 | optional int32 numberOfPages = 7; 84 | optional string subtitle = 8; 85 | repeated BookAuthor author = 9; 86 | optional string readerUrl = 10; 87 | optional string downloadEpubUrl = 11; 88 | optional string downloadPdfUrl = 12; 89 | optional string acsEpubTokenUrl = 13; 90 | optional string acsPdfTokenUrl = 14; 91 | optional bool epubAvailable = 15; 92 | optional bool pdfAvailable = 16; 93 | optional string aboutTheAuthor = 17; 94 | repeated group Identifier = 18 { 95 | optional int32 type = 19; 96 | optional string identifier = 20; 97 | } 98 | optional bool fixedLayoutContent = 21; 99 | optional bool audioVideoContent = 22; 100 | optional bool isAgencyBook = 23; 101 | } 102 | message BookSubject { 103 | optional string name = 1; 104 | optional string query = 2; 105 | optional string subjectId = 3; 106 | } 107 | message BrowseLink { 108 | optional string name = 1; 109 | optional string dataUrl = 3; 110 | optional bytes serverLogsCookie = 4; 111 | } 112 | message BrowseResponse { 113 | optional string contentsUrl = 1; 114 | optional string promoUrl = 2; 115 | repeated BrowseLink category = 3; 116 | repeated BrowseLink breadcrumb = 4; 117 | repeated QuickLink quickLink = 5; 118 | optional bytes serverLogsCookie = 6; 119 | } 120 | message QuickLink { 121 | optional string name = 1; 122 | optional Image image = 2; 123 | optional ResolvedLink link = 3; 124 | optional bool displayRequired = 4; 125 | optional bytes serverLogsCookie = 5; 126 | optional int32 backendId = 6; 127 | optional bool prismStyle = 7; 128 | } 129 | message BuyResponse { 130 | optional PurchaseNotificationResponse purchaseResponse = 1; 131 | optional group CheckoutInfo = 2 { 132 | optional LineItem item = 3; 133 | repeated LineItem subItem = 4; 134 | repeated group CheckoutOption = 5 { 135 | optional string formOfPayment = 6; 136 | optional string encodedAdjustedCart = 7; 137 | optional string instrumentId = 15; 138 | repeated LineItem item = 16; 139 | repeated LineItem subItem = 17; 140 | optional LineItem total = 18; 141 | repeated string footerHtml = 19; 142 | optional int32 instrumentFamily = 29; 143 | optional bool selectedInstrument = 32; 144 | optional LineItem summary = 33; 145 | repeated string footnoteHtml = 35; 146 | optional Instrument instrument = 43; 147 | optional string purchaseCookie = 45; 148 | repeated string disabledReason = 48; 149 | } 150 | optional string deprecatedCheckoutUrl = 10; 151 | optional string addInstrumentUrl = 11; 152 | repeated string footerHtml = 20; 153 | repeated int32 eligibleInstrumentFamily = 31; 154 | repeated string footnoteHtml = 36; 155 | repeated Instrument eligibleInstrument = 44; 156 | } 157 | optional string continueViaUrl = 8; 158 | optional string purchaseStatusUrl = 9; 159 | optional string checkoutServiceId = 12; 160 | optional bool checkoutTokenRequired = 13; 161 | optional string baseCheckoutUrl = 14; 162 | repeated string tosCheckboxHtml = 37; 163 | optional int32 permissionError = 38; 164 | optional PurchaseStatusResponse purchaseStatusResponse = 39; 165 | optional string purchaseCookie = 46; 166 | optional Challenge challenge = 49; 167 | optional string addInstrumentPromptHtml = 50; 168 | optional string confirmButtonText = 51; 169 | optional string permissionErrorTitleText = 52; 170 | optional string permissionErrorMessageText = 53; 171 | optional bytes serverLogsCookie = 54; 172 | optional string encodedDeliveryToken = 55; 173 | } 174 | message LineItem { 175 | optional string name = 1; 176 | optional string description = 2; 177 | optional Offer offer = 3; 178 | optional Money amount = 4; 179 | } 180 | message Money { 181 | optional int64 micros = 1; 182 | optional string currencyCode = 2; 183 | optional string formattedAmount = 3; 184 | } 185 | message PurchaseNotificationResponse { 186 | optional int32 status = 1; 187 | optional DebugInfo debugInfo = 2; 188 | optional string localizedErrorMessage = 3; 189 | optional string purchaseId = 4; 190 | } 191 | message PurchaseStatusResponse { 192 | optional int32 status = 1; 193 | optional string statusMsg = 2; 194 | optional string statusTitle = 3; 195 | optional string briefMessage = 4; 196 | optional string infoUrl = 5; 197 | optional LibraryUpdate libraryUpdate = 6; 198 | optional Instrument rejectedInstrument = 7; 199 | optional AndroidAppDeliveryData appDeliveryData = 8; 200 | } 201 | message BillingProfileResponse { 202 | optional int32 result = 1; 203 | optional BillingProfile billingProfile = 2; 204 | optional string userMessageHtml = 3; 205 | } 206 | message CheckInstrumentResponse { 207 | optional bool userHasValidInstrument = 1; 208 | optional bool checkoutTokenRequired = 2; 209 | repeated Instrument instrument = 4; 210 | repeated Instrument eligibleInstrument = 5; 211 | } 212 | message InstrumentSetupInfoResponse { 213 | repeated InstrumentSetupInfo setupInfo = 1; 214 | optional bool checkoutTokenRequired = 2; 215 | } 216 | message RedeemGiftCardRequest { 217 | optional string giftCardPin = 1; 218 | optional Address address = 2; 219 | repeated string acceptedLegalDocumentId = 3; 220 | optional string checkoutToken = 4; 221 | } 222 | message RedeemGiftCardResponse { 223 | optional int32 result = 1; 224 | optional string userMessageHtml = 2; 225 | optional string balanceHtml = 3; 226 | optional AddressChallenge addressChallenge = 4; 227 | optional bool checkoutTokenRequired = 5; 228 | } 229 | message UpdateInstrumentRequest { 230 | optional Instrument instrument = 1; 231 | optional string checkoutToken = 2; 232 | } 233 | message UpdateInstrumentResponse { 234 | optional int32 result = 1; 235 | optional string instrumentId = 2; 236 | optional string userMessageHtml = 3; 237 | repeated InputValidationError errorInputField = 4; 238 | optional bool checkoutTokenRequired = 5; 239 | optional RedeemedPromoOffer redeemedOffer = 6; 240 | } 241 | message InitiateAssociationResponse { 242 | optional string userToken = 1; 243 | } 244 | message VerifyAssociationResponse { 245 | optional int32 status = 1; 246 | optional Address billingAddress = 2; 247 | optional CarrierTos carrierTos = 3; 248 | optional string carrierErrorMessage = 4; 249 | } 250 | message AddressChallenge { 251 | optional string responseAddressParam = 1; 252 | optional string responseCheckboxesParam = 2; 253 | optional string title = 3; 254 | optional string descriptionHtml = 4; 255 | repeated FormCheckbox checkbox = 5; 256 | optional Address address = 6; 257 | repeated InputValidationError errorInputField = 7; 258 | optional string errorHtml = 8; 259 | repeated int32 requiredField = 9; 260 | repeated Country supportedCountry = 10; 261 | } 262 | message AuthenticationChallenge { 263 | optional int32 authenticationType = 1; 264 | optional string responseAuthenticationTypeParam = 2; 265 | optional string responseRetryCountParam = 3; 266 | optional string gaiaHeaderText = 6; 267 | optional string gaiaDescriptionTextHtml = 7; 268 | optional string gaiaFooterTextHtml = 8; 269 | optional FormCheckbox gaiaOptOutCheckbox = 9; 270 | optional string gaiaOptOutDescriptionTextHtml = 10; 271 | } 272 | message Challenge { 273 | optional AddressChallenge addressChallenge = 1; 274 | optional AuthenticationChallenge authenticationChallenge = 2; 275 | optional WebViewChallenge webViewChallenge = 3; 276 | } 277 | message Country { 278 | optional string regionCode = 1; 279 | optional string displayName = 2; 280 | } 281 | message FormCheckbox { 282 | optional string description = 1; 283 | optional bool checked = 2; 284 | optional bool required = 3; 285 | optional string id = 4; 286 | } 287 | message InputValidationError { 288 | optional int32 inputField = 1; 289 | optional string errorMessage = 2; 290 | } 291 | message WebViewChallenge { 292 | optional string startUrl = 1; 293 | optional string targetUrlRegexp = 2; 294 | optional string cancelButtonDisplayLabel = 3; 295 | optional string responseTargetUrlParam = 4; 296 | optional string cancelUrlRegexp = 5; 297 | optional string title = 6; 298 | } 299 | message AddCreditCardPromoOffer { 300 | optional string headerText = 1; 301 | optional string descriptionHtml = 2; 302 | optional Image image = 3; 303 | optional string introductoryTextHtml = 4; 304 | optional string offerTitle = 5; 305 | optional string noActionDescription = 6; 306 | optional string termsAndConditionsHtml = 7; 307 | } 308 | message AvailablePromoOffer { 309 | optional AddCreditCardPromoOffer addCreditCardOffer = 1; 310 | } 311 | message CheckPromoOfferResponse { 312 | repeated AvailablePromoOffer availableOffer = 1; 313 | optional RedeemedPromoOffer redeemedOffer = 2; 314 | optional bool checkoutTokenRequired = 3; 315 | } 316 | message RedeemedPromoOffer { 317 | optional string headerText = 1; 318 | optional string descriptionHtml = 2; 319 | optional Image image = 3; 320 | } 321 | message ActiveExperiments { 322 | repeated string clientAlteringExperiment = 1; 323 | repeated string otherExperiment = 2; 324 | repeated int32 gwsExperiment = 3; 325 | } 326 | message AndroidClientInfo { 327 | optional int64 androidId = 1; 328 | optional string loggingId = 2; 329 | optional int32 sdkVersion = 3; 330 | optional string model = 4; 331 | optional string product = 5; 332 | optional string osBuild = 6; 333 | optional string applicationBuild = 7; 334 | optional string hardware = 8; 335 | optional string device = 9; 336 | optional string mccMnc = 10; 337 | optional string locale = 11; 338 | optional string country = 12; 339 | } 340 | message ClientInfo { 341 | optional int32 clientType = 1; 342 | optional AndroidClientInfo androidClientInfo = 2; 343 | optional DesktopClientInfo desktopClientInfo = 3; 344 | optional IosClientInfo iosClientInfo = 4; 345 | } 346 | message DesktopClientInfo { 347 | optional string clientId = 1; 348 | optional string loggingId = 2; 349 | optional string os = 3; 350 | optional string osMajorVersion = 4; 351 | optional string osFullVersion = 5; 352 | optional string applicationBuild = 6; 353 | } 354 | message ExperimentIdList { 355 | repeated string id = 1; 356 | } 357 | message IosClientInfo { 358 | optional string clientId = 1; 359 | optional string loggingId = 2; 360 | optional string osMajorVersion = 3; 361 | optional string osFullVersion = 4; 362 | optional string applicationBuild = 5; 363 | } 364 | message LogEvent { 365 | optional int64 eventTimeMs = 1; 366 | optional string tag = 2; 367 | repeated LogEventKeyValues value = 3; 368 | optional PlayStoreLogEvent store = 4; 369 | optional bytes sourceExtension = 6; 370 | optional ActiveExperiments exp = 7; 371 | } 372 | message LogEventKeyValues { 373 | optional string key = 1; 374 | optional string value = 2; 375 | } 376 | message LogRequest { 377 | optional ClientInfo clientInfo = 1; 378 | optional int32 logSource = 2; 379 | repeated LogEvent logEvent = 3; 380 | optional int64 requestTimeMs = 4; 381 | optional bytes serializedLogEvents = 5; 382 | } 383 | message LogResponse { 384 | optional int64 nextRequestWaitMillis = 1; 385 | optional ExperimentIdList experiments = 2; 386 | } 387 | message Docid { 388 | optional string backendDocid = 1; 389 | optional int32 type = 2; 390 | optional int32 backend = 3; 391 | } 392 | message Install { 393 | optional fixed64 androidId = 1; 394 | optional int32 version = 2; 395 | optional bool bundled = 3; 396 | optional bool pending = 4; 397 | } 398 | 399 | message GroupLicenseKey { 400 | } 401 | message LicenseTerms { 402 | optional GroupLicenseKey groupLicenseKey = 1; 403 | } 404 | 405 | message Offer { 406 | optional int64 micros = 1; 407 | optional string currencyCode = 2; 408 | optional string formattedAmount = 3; 409 | repeated Offer convertedPrice = 4; 410 | optional bool checkoutFlowRequired = 5; 411 | optional int64 fullPriceMicros = 6; 412 | optional string formattedFullAmount = 7; 413 | optional int32 offerType = 8; 414 | optional RentalTerms rentalTerms = 9; 415 | optional int64 onSaleDate = 10; 416 | repeated string promotionLabel = 11; 417 | optional SubscriptionTerms subscriptionTerms = 12; 418 | optional string formattedName = 13; 419 | optional string formattedDescription = 14; 420 | optional bool preorder = 15; 421 | optional int32 onSaleDateDisplayTimeZoneOffsetMsec = 16; 422 | optional int32 licensedOfferType = 17; 423 | optional SubscriptionContentTerms subscriptionContentTerms = 18; 424 | optional string offerId = 19; 425 | optional int64 preorderFulfillmentDisplayDate = 20; 426 | optional LicenseTerms licenseTerms = 21; 427 | optional bool temporarilyFree = 22; 428 | optional VoucherTerms voucherTerms = 23; 429 | repeated OfferPayment offerPayment = 24; 430 | optional bool repeatLastPayment = 25; 431 | optional string buyButtonLabel = 26; 432 | optional bool instantPurchaseEnabled = 27; 433 | } 434 | 435 | message MonthAndDay { 436 | optional uint32 month = 1; 437 | optional uint32 day = 2; 438 | } 439 | message OfferPaymentPeriod { 440 | optional TimePeriod duration = 1; 441 | optional MonthAndDay start = 2; 442 | optional MonthAndDay end = 3; 443 | } 444 | message OfferPaymentOverride { 445 | optional int64 micros = 1; 446 | optional MonthAndDay start = 2; 447 | optional MonthAndDay end = 3; 448 | } 449 | message OfferPayment { 450 | optional int64 micros = 1; 451 | optional string currencyCode = 2; 452 | optional OfferPaymentPeriod offerPaymentPeriod = 3; 453 | repeated OfferPaymentOverride offerPaymentOverride = 4; 454 | } 455 | message VoucherTerms { 456 | } 457 | message RentalTerms { 458 | optional int32 dEPRECATEDGrantPeriodSeconds = 1; 459 | optional int32 dEPRECATEDActivatePeriodSeconds = 2; 460 | optional TimePeriod grantPeriod = 3; 461 | optional TimePeriod activatePeriod = 4; 462 | } 463 | message SignedData { 464 | optional string signedData = 1; 465 | optional string signature = 2; 466 | } 467 | message SubscriptionContentTerms { 468 | optional Docid requiredSubscription = 1; 469 | } 470 | message SubscriptionTerms { 471 | optional TimePeriod recurringPeriod = 1; 472 | optional TimePeriod trialPeriod = 2; 473 | } 474 | message TimePeriod { 475 | optional int32 unit = 1; 476 | optional int32 count = 2; 477 | } 478 | message BillingAddressSpec { 479 | optional int32 billingAddressType = 1; 480 | repeated int32 requiredField = 2; 481 | } 482 | message BillingProfile { 483 | repeated Instrument instrument = 1; 484 | optional string selectedExternalInstrumentId = 2; 485 | repeated BillingProfileOption billingProfileOption = 3; 486 | } 487 | message BillingProfileOption { 488 | optional int32 type = 1; 489 | optional string displayTitle = 2; 490 | optional string externalInstrumentId = 3; 491 | optional TopupInfo topupInfo = 4; 492 | optional CarrierBillingInstrumentStatus carrierBillingInstrumentStatus = 5; 493 | } 494 | message CarrierBillingCredentials { 495 | optional string value = 1; 496 | optional int64 expiration = 2; 497 | } 498 | message CarrierBillingInstrument { 499 | optional string instrumentKey = 1; 500 | optional string accountType = 2; 501 | optional string currencyCode = 3; 502 | optional int64 transactionLimit = 4; 503 | optional string subscriberIdentifier = 5; 504 | optional EncryptedSubscriberInfo encryptedSubscriberInfo = 6; 505 | optional CarrierBillingCredentials credentials = 7; 506 | optional CarrierTos acceptedCarrierTos = 8; 507 | } 508 | message CarrierBillingInstrumentStatus { 509 | optional CarrierTos carrierTos = 1; 510 | optional bool associationRequired = 2; 511 | optional bool passwordRequired = 3; 512 | optional PasswordPrompt carrierPasswordPrompt = 4; 513 | optional int32 apiVersion = 5; 514 | optional string name = 6; 515 | optional DeviceAssociation deviceAssociation = 7; 516 | optional string carrierSupportPhoneNumber = 8; 517 | } 518 | message CarrierTos { 519 | optional CarrierTosEntry dcbTos = 1; 520 | optional CarrierTosEntry piiTos = 2; 521 | optional bool needsDcbTosAcceptance = 3; 522 | optional bool needsPiiTosAcceptance = 4; 523 | } 524 | message CarrierTosEntry { 525 | optional string url = 1; 526 | optional string version = 2; 527 | } 528 | message CreditCardInstrument { 529 | optional int32 type = 1; 530 | optional string escrowHandle = 2; 531 | optional string lastDigits = 3; 532 | optional int32 expirationMonth = 4; 533 | optional int32 expirationYear = 5; 534 | repeated EfeParam escrowEfeParam = 6; 535 | } 536 | message DeviceAssociation { 537 | optional string userTokenRequestMessage = 1; 538 | optional string userTokenRequestAddress = 2; 539 | } 540 | message DisabledInfo { 541 | optional int32 disabledReason = 1; 542 | optional string disabledMessageHtml = 2; 543 | optional string errorMessage = 3; 544 | } 545 | message EfeParam { 546 | optional int32 key = 1; 547 | optional string value = 2; 548 | } 549 | message Instrument { 550 | optional string externalInstrumentId = 1; 551 | optional Address billingAddress = 2; 552 | optional CreditCardInstrument creditCard = 3; 553 | optional CarrierBillingInstrument carrierBilling = 4; 554 | optional BillingAddressSpec billingAddressSpec = 5; 555 | optional int32 instrumentFamily = 6; 556 | optional CarrierBillingInstrumentStatus carrierBillingStatus = 7; 557 | optional string displayTitle = 8; 558 | optional TopupInfo topupInfoDeprecated = 9; 559 | optional int32 version = 10; 560 | optional StoredValueInstrument storedValue = 11; 561 | repeated DisabledInfo disabledInfo = 12; 562 | } 563 | message InstrumentSetupInfo { 564 | optional int32 instrumentFamily = 1; 565 | optional bool supported = 2; 566 | optional AddressChallenge addressChallenge = 3; 567 | optional Money balance = 4; 568 | repeated string footerHtml = 5; 569 | } 570 | message PasswordPrompt { 571 | optional string prompt = 1; 572 | optional string forgotPasswordUrl = 2; 573 | } 574 | message StoredValueInstrument { 575 | optional int32 type = 1; 576 | optional Money balance = 2; 577 | optional TopupInfo topupInfo = 3; 578 | } 579 | message TopupInfo { 580 | optional string optionsContainerDocidDeprecated = 1; 581 | optional string optionsListUrl = 2; 582 | optional string subtitle = 3; 583 | optional Docid optionsContainerDocid = 4; 584 | } 585 | message ConsumePurchaseResponse { 586 | optional LibraryUpdate libraryUpdate = 1; 587 | optional int32 status = 2; 588 | } 589 | message ContainerMetadata { 590 | optional string browseUrl = 1; 591 | optional string nextPageUrl = 2; 592 | optional double relevance = 3; 593 | optional int64 estimatedResults = 4; 594 | optional string analyticsCookie = 5; 595 | optional bool ordered = 6; 596 | repeated ContainerView containerView = 7; 597 | } 598 | message ContainerView { 599 | optional bool selected = 1; 600 | optional string title = 2; 601 | optional string listUrl = 3; 602 | optional bytes serverLogsCookie = 4; 603 | } 604 | message FlagContentResponse { 605 | } 606 | message ClientDownloadRequest { 607 | message ApkInfo { 608 | optional string packageName = 1; 609 | optional int32 versionCode = 2; 610 | } 611 | message CertificateChain { 612 | message Element { 613 | optional bytes certificate = 1; 614 | optional bool parsedSuccessfully = 2; 615 | optional bytes subject = 3; 616 | optional bytes issuer = 4; 617 | optional bytes fingerprint = 5; 618 | optional int64 expiryTime = 6; 619 | optional int64 startTime = 7; 620 | } 621 | repeated Element element = 1; 622 | } 623 | message Digests { 624 | optional bytes sha256 = 1; 625 | optional bytes sha1 = 2; 626 | optional bytes md5 = 3; 627 | } 628 | message Resource { 629 | optional string url = 1; 630 | optional int32 type = 2; 631 | optional bytes remoteIp = 3; 632 | optional string referrer = 4; 633 | } 634 | message SignatureInfo { 635 | repeated CertificateChain certificateChain = 1; 636 | optional bool trusted = 2; 637 | } 638 | optional string url = 1; 639 | optional Digests digests = 2; 640 | optional int64 length = 3; 641 | repeated Resource resources = 4; 642 | optional SignatureInfo signature = 5; 643 | optional bool userInitiated = 6; 644 | repeated string clientAsn = 8; 645 | optional string fileBasename = 9; 646 | optional int32 downloadType = 10; 647 | optional string locale = 11; 648 | optional ApkInfo apkInfo = 12; 649 | optional fixed64 androidId = 13; 650 | repeated string originatingPackages = 15; 651 | optional SignatureInfo originatingSignature = 17; 652 | } 653 | message ClientDownloadResponse { 654 | message MoreInfo { 655 | optional string description = 1; 656 | optional string url = 2; 657 | } 658 | optional int32 verdict = 1; 659 | optional MoreInfo moreInfo = 2; 660 | optional bytes token = 3; 661 | } 662 | message ClientDownloadStatsRequest { 663 | optional int32 userDecision = 1; 664 | optional bytes token = 2; 665 | } 666 | message DebugInfo { 667 | repeated string message = 1; 668 | repeated group Timing = 2 { 669 | optional string name = 3; 670 | optional double timeInMs = 4; 671 | } 672 | } 673 | message DebugSettingsResponse { 674 | optional string playCountryOverride = 1; 675 | optional string playCountryDebugInfo = 2; 676 | } 677 | message DeliveryResponse { 678 | optional int32 status = 1; 679 | optional AndroidAppDeliveryData appDeliveryData = 2; 680 | } 681 | message BulkDetailsEntry { 682 | optional DocV2 doc = 1; 683 | } 684 | message BulkDetailsRequest { 685 | repeated string docid = 1; 686 | optional bool includeChildDocs = 2; 687 | optional bool includeDetails = 3; 688 | optional string sourcePackageName = 4; 689 | repeated int32 installedVersionCode = 5; 690 | } 691 | message BulkDetailsResponse { 692 | repeated BulkDetailsEntry entry = 1; 693 | } 694 | message DetailsResponse { 695 | optional DocV1 docV1 = 1; 696 | optional string analyticsCookie = 2; 697 | optional Review userReview = 3; 698 | optional DocV2 docV2 = 4; 699 | optional string footerHtml = 5; 700 | optional bytes serverLogsCookie = 6; 701 | repeated DiscoveryBadge discoveryBadge = 7; 702 | } 703 | 704 | message DiscoveryBadge { 705 | optional string title = 1; 706 | optional Image image = 2; 707 | optional int32 backgroundColor = 3; 708 | optional DiscoveryBadgeLink discoveryBadgeLink = 4; 709 | optional bytes serverLogsCookie = 5; 710 | optional bool isPlusOne = 6; 711 | optional float aggregateRating = 7; 712 | optional int32 userStarRating = 8; 713 | optional string downloadCount = 9; 714 | optional string downloadUnits = 10; 715 | optional string contentDescription = 11; 716 | } 717 | 718 | message DiscoveryBadgeLink { 719 | optional Link link = 1; 720 | optional string userReviewsUrl = 2; 721 | optional string criticReviewsUrl = 3; 722 | } 723 | 724 | message DeviceConfigurationProto { 725 | optional int32 touchScreen = 1; 726 | optional int32 keyboard = 2; 727 | optional int32 navigation = 3; 728 | optional int32 screenLayout = 4; 729 | optional bool hasHardKeyboard = 5; 730 | optional bool hasFiveWayNavigation = 6; 731 | optional int32 screenDensity = 7; 732 | optional int32 glEsVersion = 8; 733 | repeated string systemSharedLibrary = 9; 734 | repeated string systemAvailableFeature = 10; 735 | repeated string nativePlatform = 11; 736 | optional int32 screenWidth = 12; 737 | optional int32 screenHeight = 13; 738 | repeated string systemSupportedLocale = 14; 739 | repeated string glExtension = 15; 740 | optional int32 deviceClass = 16; 741 | optional int32 maxApkDownloadSizeMb = 17; 742 | } 743 | message Document { 744 | optional Docid docid = 1; 745 | optional Docid fetchDocid = 2; 746 | optional Docid sampleDocid = 3; 747 | optional string title = 4; 748 | optional string url = 5; 749 | repeated string snippet = 6; 750 | optional Offer priceDeprecated = 7; 751 | optional Availability availability = 9; 752 | repeated Image image = 10; 753 | repeated Document child = 11; 754 | optional AggregateRating aggregateRating = 13; 755 | repeated Offer offer = 14; 756 | repeated TranslatedText translatedSnippet = 15; 757 | repeated DocumentVariant documentVariant = 16; 758 | repeated string categoryId = 17; 759 | repeated Document decoration = 18; 760 | repeated Document parent = 19; 761 | optional string privacyPolicyUrl = 20; 762 | optional string consumptionUrl = 21; 763 | optional int32 estimatedNumChildren = 22; 764 | optional string subtitle = 23; 765 | } 766 | message DocumentVariant { 767 | optional int32 variationType = 1; 768 | optional Rule rule = 2; 769 | optional string title = 3; 770 | repeated string snippet = 4; 771 | optional string recentChanges = 5; 772 | repeated TranslatedText autoTranslation = 6; 773 | repeated Offer offer = 7; 774 | optional int64 channelId = 9; 775 | repeated Document child = 10; 776 | repeated Document decoration = 11; 777 | repeated Image image = 12; 778 | repeated string categoryId = 13; 779 | optional string subtitle = 14; 780 | } 781 | message Image { 782 | optional int32 imageType = 1; 783 | optional group Dimension = 2 { 784 | optional int32 width = 3; 785 | optional int32 height = 4; 786 | } 787 | optional string imageUrl = 5; 788 | optional string altTextLocalized = 6; 789 | optional string secureUrl = 7; 790 | optional int32 positionInSequence = 8; 791 | optional bool supportsFifeUrlOptions = 9; 792 | optional group Citation = 10 { 793 | optional string titleLocalized = 11; 794 | optional string url = 12; 795 | } 796 | optional int32 durationSeconds = 14; 797 | optional string fillColorRgb = 15; 798 | optional bool autogen = 16; 799 | optional Attribution attribution = 17; 800 | optional string backgroundColorRgb = 19; 801 | optional ImagePalette palette = 20; 802 | optional int32 deviceClass = 21; 803 | optional bool supportsFifeMonogramOption = 22; 804 | } 805 | message ImagePalette { 806 | } 807 | message Attribution { 808 | optional string sourceTitle = 1; 809 | optional string sourceUrl = 2; 810 | optional string licenseTitle = 3; 811 | optional string licenseUrl = 4; 812 | } 813 | message TranslatedText { 814 | optional string text = 1; 815 | optional string sourceLocale = 2; 816 | optional string targetLocale = 3; 817 | } 818 | message Badge { 819 | optional string title = 1; 820 | repeated Image image = 2; 821 | optional string browseUrl = 3; 822 | optional string description = 4; 823 | } 824 | message BadgeContainer { 825 | optional string title = 1; 826 | repeated Image image = 2; 827 | repeated Badge badge = 3; 828 | } 829 | message ContainerWithBanner { 830 | optional string colorThemeArgb = 1; 831 | } 832 | message DealOfTheDay { 833 | optional string featuredHeader = 1; 834 | optional string colorThemeArgb = 2; 835 | } 836 | message Dismissal { 837 | optional string url = 1; 838 | optional string descriptionHtml = 2; 839 | } 840 | message EditorialSeriesContainer { 841 | optional string seriesTitle = 1; 842 | optional string seriesSubtitle = 2; 843 | optional string episodeTitle = 3; 844 | optional string episodeSubtitle = 4; 845 | optional string colorThemeArgb = 5; 846 | repeated VideoSnippet videoSnippet = 6; 847 | } 848 | message Link { 849 | optional string uri = 1; 850 | optional ResolvedLink resolvedLink = 2; 851 | optional int32 uriBackend = 3; 852 | } 853 | message NextBanner { 854 | optional string title = 1; 855 | optional string subtitle = 2; 856 | optional string colorTextArgb = 3; 857 | } 858 | message OBSOLETE_Reason { 859 | optional string briefReason = 1; 860 | optional string oBSOLETEDetailedReason = 2; 861 | optional string uniqueId = 3; 862 | } 863 | message PlusOneData { 864 | optional bool setByUser = 1; 865 | optional int64 total = 2; 866 | optional int64 circlesTotal = 3; 867 | repeated PlusProfile circlesProfiles = 4; 868 | } 869 | message PromotedDoc { 870 | optional string title = 1; 871 | optional string subtitle = 2; 872 | repeated Image image = 3; 873 | optional string descriptionHtml = 4; 874 | optional string detailsUrl = 5; 875 | } 876 | message Reason { 877 | optional string descriptionHtml = 3; 878 | optional ReasonPlusProfiles reasonPlusProfiles = 4; 879 | optional ReasonReview reasonReview = 5; 880 | optional Dismissal dismissal = 7; 881 | } 882 | message ReasonPlusProfiles { 883 | optional string localizedDescriptionHtml = 1; 884 | repeated PlusProfile plusProfile = 2; 885 | } 886 | message ReasonReview { 887 | optional Review review = 1; 888 | } 889 | message RecommendationsContainer { 890 | } 891 | message SectionMetadata { 892 | optional string header = 1; 893 | optional string listUrl = 2; 894 | optional string browseUrl = 3; 895 | optional string descriptionHtml = 4; 896 | } 897 | message SeriesAntenna { 898 | optional string seriesTitle = 1; 899 | optional string seriesSubtitle = 2; 900 | optional string episodeTitle = 3; 901 | optional string episodeSubtitle = 4; 902 | optional string colorThemeArgb = 5; 903 | optional SectionMetadata sectionTracks = 6; 904 | optional SectionMetadata sectionAlbums = 7; 905 | } 906 | message SuggestionReasons { 907 | repeated Reason reason = 2; 908 | optional Dismissal neutralDismissal = 4; 909 | optional Dismissal positiveDismissal = 5; 910 | } 911 | message Template { 912 | optional SeriesAntenna seriesAntenna = 1; 913 | optional TileTemplate tileGraphic2X1 = 2; 914 | optional TileTemplate tileGraphic4X2 = 3; 915 | optional TileTemplate tileGraphicColoredTitle2X1 = 4; 916 | optional TileTemplate tileGraphicUpperLeftTitle2X1 = 5; 917 | optional TileTemplate tileDetailsReflectedGraphic2X2 = 6; 918 | optional TileTemplate tileFourBlock4X2 = 7; 919 | optional ContainerWithBanner containerWithBanner = 8; 920 | optional DealOfTheDay dealOfTheDay = 9; 921 | optional TileTemplate tileGraphicColoredTitle4X2 = 10; 922 | optional EditorialSeriesContainer editorialSeriesContainer = 11; 923 | optional RecommendationsContainer recommendationsContainer = 12; 924 | optional NextBanner nextBanner = 13; 925 | } 926 | message TileTemplate { 927 | optional string colorThemeArgb = 1; 928 | optional string colorTextArgb = 2; 929 | } 930 | message VideoSnippet { 931 | repeated Image image = 1; 932 | optional string title = 2; 933 | optional string description = 3; 934 | } 935 | message Warning { 936 | optional string localizedMessage = 1; 937 | } 938 | message AlbumDetails { 939 | optional string name = 1; 940 | optional MusicDetails details = 2; 941 | optional ArtistDetails displayArtist = 3; 942 | } 943 | 944 | message Dependency { 945 | optional string packageName = 1; 946 | optional int32 minVersionCode = 2; 947 | optional int32 versionCodeMask = 3; 948 | optional bool skipPermissions = 4; 949 | } 950 | 951 | message InstallDetails { 952 | optional int32 installLocation = 1; 953 | optional int64 size = 2; 954 | optional Dependency dependency = 3; 955 | optional int32 targetSdkVersion = 4; 956 | } 957 | 958 | message AppDetails { 959 | optional string developerName = 1; 960 | optional int32 majorVersionNumber = 2; 961 | optional int32 versionCode = 3; 962 | optional string versionString = 4; 963 | optional string title = 5; 964 | repeated string appCategory = 7; 965 | optional int32 contentRating = 8; 966 | optional int64 installationSize = 9; 967 | repeated string permission = 10; 968 | optional string developerEmail = 11; 969 | optional string developerWebsite = 12; 970 | optional string numDownloads = 13; 971 | optional string packageName = 14; 972 | optional string recentChangesHtml = 15; 973 | optional string uploadDate = 16; 974 | repeated FileMetadata file = 17; 975 | optional string appType = 18; 976 | repeated string certificateHash = 19; 977 | optional bool variesByAccount = 21; 978 | repeated CertificateSet certificateSet = 22; 979 | repeated string autoAcquireFreeAppIfHigherVersionAvailableTag = 23; 980 | optional bool declaresIab = 24; 981 | repeated string splitId = 25; 982 | optional bool gamepadRequired = 26; 983 | optional bool externallyHosted = 27; 984 | optional bool everExternallyHosted = 28; 985 | optional string installNotes = 30; 986 | optional int32 installLocation = 31; 987 | optional int32 targetSdkVersion = 32; 988 | optional string hasPreregistrationPromoCode = 33; 989 | optional InstallDetails installDetails = 34; 990 | } 991 | 992 | message CertificateSet { 993 | repeated string certificateHash = 1; 994 | } 995 | 996 | message AppPermission { 997 | optional string key = 1; 998 | optional bool permissionRequired = 2; 999 | } 1000 | message ArtistDetails { 1001 | optional string detailsUrl = 1; 1002 | optional string name = 2; 1003 | optional ArtistExternalLinks externalLinks = 3; 1004 | } 1005 | message ArtistExternalLinks { 1006 | repeated string websiteUrl = 1; 1007 | optional string googlePlusProfileUrl = 2; 1008 | optional string youtubeChannelUrl = 3; 1009 | } 1010 | message DocumentDetails { 1011 | optional AppDetails appDetails = 1; 1012 | optional AlbumDetails albumDetails = 2; 1013 | optional ArtistDetails artistDetails = 3; 1014 | optional SongDetails songDetails = 4; 1015 | optional BookDetails bookDetails = 5; 1016 | optional VideoDetails videoDetails = 6; 1017 | optional SubscriptionDetails subscriptionDetails = 7; 1018 | optional MagazineDetails magazineDetails = 8; 1019 | optional TvShowDetails tvShowDetails = 9; 1020 | optional TvSeasonDetails tvSeasonDetails = 10; 1021 | optional TvEpisodeDetails tvEpisodeDetails = 11; 1022 | // optional NewsDetails newsDetails = 12; 1023 | } 1024 | 1025 | message PatchDetails { 1026 | optional int32 baseVersionCode = 1; 1027 | optional int64 size = 2; 1028 | } 1029 | 1030 | message FileMetadata { 1031 | optional int32 fileType = 1; 1032 | optional int32 versionCode = 2; 1033 | optional int64 size = 3; 1034 | optional string splitId = 4; 1035 | optional int64 compressedSize = 5; 1036 | repeated PatchDetails patchDetails = 6; 1037 | } 1038 | message MagazineDetails { 1039 | optional string parentDetailsUrl = 1; 1040 | optional string deviceAvailabilityDescriptionHtml = 2; 1041 | optional string psvDescription = 3; 1042 | optional string deliveryFrequencyDescription = 4; 1043 | } 1044 | message MusicDetails { 1045 | optional int32 censoring = 1; 1046 | optional int32 durationSec = 2; 1047 | optional string originalReleaseDate = 3; 1048 | optional string label = 4; 1049 | repeated ArtistDetails artist = 5; 1050 | repeated string genre = 6; 1051 | optional string releaseDate = 7; 1052 | repeated int32 releaseType = 8; 1053 | } 1054 | message NewsDetails { 1055 | optional string parentDetailsUrl = 1; 1056 | optional string psvDescription = 3; 1057 | } 1058 | message SongDetails { 1059 | optional string name = 1; 1060 | optional MusicDetails details = 2; 1061 | optional string albumName = 3; 1062 | optional int32 trackNumber = 4; 1063 | optional string previewUrl = 5; 1064 | optional ArtistDetails displayArtist = 6; 1065 | optional Badge badge = 7; 1066 | } 1067 | message SubscriptionDetails { 1068 | optional int32 subscriptionPeriod = 1; 1069 | } 1070 | message Trailer { 1071 | optional string trailerId = 1; 1072 | optional string title = 2; 1073 | optional string thumbnailUrl = 3; 1074 | optional string watchUrl = 4; 1075 | optional string duration = 5; 1076 | } 1077 | message TvEpisodeDetails { 1078 | optional string parentDetailsUrl = 1; 1079 | optional int32 episodeIndex = 2; 1080 | optional string releaseDate = 3; 1081 | } 1082 | message TvSeasonDetails { 1083 | optional string parentDetailsUrl = 1; 1084 | optional int32 seasonIndex = 2; 1085 | optional string releaseDate = 3; 1086 | optional string broadcaster = 4; 1087 | optional int32 episodeCount = 5; 1088 | optional int32 expectedEpisodeCount = 6; 1089 | } 1090 | message TvShowDetails { 1091 | optional int32 seasonCount = 1; 1092 | optional int32 startYear = 2; 1093 | optional int32 endYear = 3; 1094 | optional string broadcaster = 4; 1095 | } 1096 | message VideoCredit { 1097 | optional int32 creditType = 1; 1098 | optional string credit = 2; 1099 | repeated string name = 3; 1100 | } 1101 | message VideoDetails { 1102 | repeated VideoCredit credit = 1; 1103 | optional string duration = 2; 1104 | optional string releaseDate = 3; 1105 | optional string contentRating = 4; 1106 | optional int64 likes = 5; 1107 | optional int64 dislikes = 6; 1108 | repeated string genre = 7; 1109 | repeated Trailer trailer = 8; 1110 | repeated VideoRentalTerm rentalTerm = 9; 1111 | repeated string audioLanguage = 10; 1112 | repeated string captionLanguage = 11; 1113 | } 1114 | message VideoRentalTerm { 1115 | optional int32 offerType = 1; 1116 | optional string offerAbbreviation = 2; 1117 | optional string rentalHeader = 3; 1118 | repeated group Term = 4 { 1119 | optional string header = 5; 1120 | optional string body = 6; 1121 | } 1122 | } 1123 | message Bucket { 1124 | repeated DocV1 document = 1; 1125 | optional bool multiCorpus = 2; 1126 | optional string title = 3; 1127 | optional string iconUrl = 4; 1128 | optional string fullContentsUrl = 5; 1129 | optional double relevance = 6; 1130 | optional int64 estimatedResults = 7; 1131 | optional string analyticsCookie = 8; 1132 | optional string fullContentsListUrl = 9; 1133 | optional string nextPageUrl = 10; 1134 | optional bool ordered = 11; 1135 | } 1136 | message ListResponse { 1137 | repeated Bucket bucket = 1; 1138 | repeated DocV2 doc = 2; 1139 | } 1140 | message DocV1 { 1141 | optional Document finskyDoc = 1; 1142 | optional string docid = 2; 1143 | optional string detailsUrl = 3; 1144 | optional string reviewsUrl = 4; 1145 | optional string relatedListUrl = 5; 1146 | optional string moreByListUrl = 6; 1147 | optional string shareUrl = 7; 1148 | optional string creator = 8; 1149 | optional DocumentDetails details = 9; 1150 | optional string descriptionHtml = 10; 1151 | optional string relatedBrowseUrl = 11; 1152 | optional string moreByBrowseUrl = 12; 1153 | optional string relatedHeader = 13; 1154 | optional string moreByHeader = 14; 1155 | optional string title = 15; 1156 | optional PlusOneData plusOneData = 16; 1157 | optional string warningMessage = 17; 1158 | } 1159 | message Annotations { 1160 | optional SectionMetadata sectionRelated = 1; 1161 | optional SectionMetadata sectionMoreBy = 2; 1162 | optional PlusOneData plusOneData = 3; 1163 | repeated Warning warning = 4; 1164 | optional SectionMetadata sectionBodyOfWork = 5; 1165 | optional SectionMetadata sectionCoreContent = 6; 1166 | optional Template template = 7; 1167 | repeated Badge badgeForCreator = 8; 1168 | repeated Badge badgeForDoc = 9; 1169 | optional Link link = 10; 1170 | optional SectionMetadata sectionCrossSell = 11; 1171 | optional SectionMetadata sectionRelatedDocType = 12; 1172 | repeated PromotedDoc promotedDoc = 13; 1173 | optional string offerNote = 14; 1174 | repeated DocV2 subscription = 16; 1175 | optional OBSOLETE_Reason oBSOLETEReason = 17; 1176 | optional string privacyPolicyUrl = 18; 1177 | optional SuggestionReasons suggestionReasons = 19; 1178 | optional Warning optimalDeviceClassWarning = 20; 1179 | repeated BadgeContainer docBadgeContainer = 21; 1180 | optional SectionMetadata sectionSuggestForRating = 22; 1181 | optional SectionMetadata sectionRateAndReview = 23; 1182 | optional SectionMetadata sectionPurchaseCrossSell = 24; 1183 | repeated OverflowLink overflowLink = 25; 1184 | optional DocV2 creatorDoc = 26; 1185 | } 1186 | 1187 | message OverflowLink { 1188 | optional string title = 1; 1189 | optional Link link = 2; 1190 | } 1191 | 1192 | message ReviewTip { 1193 | optional string tipUrl = 1; 1194 | optional string text = 2; 1195 | optional int32 polarity = 3; 1196 | optional int64 reviewCount = 4; 1197 | } 1198 | 1199 | message DocV2 { 1200 | optional string docid = 1; 1201 | optional string backendDocid = 2; 1202 | optional int32 docType = 3; 1203 | optional int32 backendId = 4; 1204 | optional string title = 5; 1205 | optional string creator = 6; 1206 | optional string descriptionHtml = 7; 1207 | repeated Offer offer = 8; 1208 | optional Availability availability = 9; 1209 | repeated Image image = 10; 1210 | repeated DocV2 child = 11; 1211 | optional ContainerMetadata containerMetadata = 12; 1212 | optional DocumentDetails details = 13; 1213 | optional AggregateRating aggregateRating = 14; 1214 | optional Annotations annotations = 15; 1215 | optional string detailsUrl = 16; 1216 | optional string shareUrl = 17; 1217 | optional string reviewsUrl = 18; 1218 | optional string backendUrl = 19; 1219 | optional string purchaseDetailsUrl = 20; 1220 | optional bool detailsReusable = 21; 1221 | optional string subtitle = 22; 1222 | optional string translatedDescriptionHtml = 23; 1223 | optional bytes serverLogsCookie = 24; 1224 | optional ProductDetails productDetails = 25; 1225 | optional bool mature = 26; 1226 | optional string promotionalDescription = 27; 1227 | optional bool availabileForPreregistration = 29; 1228 | repeated ReviewTip tip = 30; 1229 | optional string snippetsUrl = 31; 1230 | optional bool forceShareability = 32; 1231 | optional bool useWishlistAsPrimaryAction = 33; 1232 | } 1233 | 1234 | message ProductDetails { 1235 | optional string title = 1; 1236 | repeated ProductDetailsSection section = 2; 1237 | } 1238 | 1239 | message ProductDetailsSection { 1240 | optional string title = 1; 1241 | repeated ProductDetailsDescription description = 3; 1242 | } 1243 | 1244 | message ProductDetailsDescription { 1245 | optional Image image = 1; 1246 | optional string description = 2; 1247 | } 1248 | 1249 | 1250 | message EncryptedSubscriberInfo { 1251 | optional string data = 1; 1252 | optional string encryptedKey = 2; 1253 | optional string signature = 3; 1254 | optional string initVector = 4; 1255 | optional int32 googleKeyVersion = 5; 1256 | optional int32 carrierKeyVersion = 6; 1257 | } 1258 | 1259 | message Availability { 1260 | optional int32 restriction = 5; 1261 | optional int32 offerType = 6; 1262 | optional Rule rule = 7; 1263 | repeated group PerDeviceAvailabilityRestriction = 9 { 1264 | optional fixed64 androidId = 10; 1265 | optional int32 deviceRestriction = 11; 1266 | optional int64 channelId = 12; 1267 | optional FilterEvaluationInfo filterInfo = 15; 1268 | } 1269 | optional bool availableIfOwned = 13; 1270 | repeated Install install = 14; 1271 | optional FilterEvaluationInfo filterInfo = 16; 1272 | optional OwnershipInfo ownershipInfo = 17; 1273 | repeated AvailabilityProblem availabilityProblem = 18; 1274 | optional bool hidden = 21; 1275 | } 1276 | 1277 | message AvailabilityProblem { 1278 | optional int32 problemType = 1; 1279 | repeated string missingValue = 2; 1280 | } 1281 | 1282 | message FilterEvaluationInfo { 1283 | repeated RuleEvaluation ruleEvaluation = 1; 1284 | } 1285 | message Rule { 1286 | optional bool negate = 1; 1287 | optional int32 operator = 2; 1288 | optional int32 key = 3; 1289 | repeated string stringArg = 4; 1290 | repeated int64 longArg = 5; 1291 | repeated double doubleArg = 6; 1292 | repeated Rule subrule = 7; 1293 | optional int32 responseCode = 8; 1294 | optional string comment = 9; 1295 | repeated fixed64 stringArgHash = 10; 1296 | repeated int32 constArg = 11; 1297 | optional int32 availabilityProblemType = 12; 1298 | optional bool includeMissingValues = 13; 1299 | } 1300 | message RuleEvaluation { 1301 | optional Rule rule = 1; 1302 | repeated string actualStringValue = 2; 1303 | repeated int64 actualLongValue = 3; 1304 | repeated bool actualBoolValue = 4; 1305 | repeated double actualDoubleValue = 5; 1306 | } 1307 | message GroupLicenseInfo { 1308 | optional int32 licensedOfferType = 1; 1309 | optional fixed64 gaiaGroupId = 2; 1310 | } 1311 | message LicensedDocumentInfo { 1312 | repeated fixed64 gaiaGroupId = 1; 1313 | } 1314 | message LibraryAppDetails { 1315 | optional string certificateHash = 2; 1316 | optional int64 refundTimeoutTimestampMsec = 3; 1317 | optional int64 postDeliveryRefundWindowMsec = 4; 1318 | } 1319 | message LibraryInAppDetails { 1320 | optional string signedPurchaseData = 1; 1321 | optional string signature = 2; 1322 | } 1323 | message LibraryMutation { 1324 | optional Docid docid = 1; 1325 | optional int32 offerType = 2; 1326 | optional int64 documentHash = 3; 1327 | optional bool deleted = 4; 1328 | optional LibraryAppDetails appDetails = 5; 1329 | optional LibrarySubscriptionDetails subscriptionDetails = 6; 1330 | optional LibraryInAppDetails inAppDetails = 7; 1331 | optional int64 validUntilTimestampMsec = 8; 1332 | } 1333 | message LibrarySubscriptionDetails { 1334 | optional int64 initiationTimestampMsec = 1; 1335 | optional int64 deprecatedValidUntilTimestampMsec = 2; 1336 | optional bool autoRenewing = 3; 1337 | optional int64 trialUntilTimestampMsec = 4; 1338 | optional string signedPurchaseData = 5; 1339 | optional string signature = 6; 1340 | } 1341 | message LibraryUpdate { 1342 | optional int32 status = 1; 1343 | optional int32 corpus = 2; 1344 | optional bytes serverToken = 3; 1345 | repeated LibraryMutation mutation = 4; 1346 | optional bool hasMore = 5; 1347 | optional string libraryId = 6; 1348 | } 1349 | message ClientLibraryState { 1350 | optional int32 corpus = 1; 1351 | optional bytes serverToken = 2; 1352 | optional int64 hashCodeSum = 3; 1353 | optional int32 librarySize = 4; 1354 | optional string libraryId = 5; 1355 | } 1356 | message LibraryReplicationRequest { 1357 | repeated ClientLibraryState libraryState = 1; 1358 | } 1359 | message LibraryReplicationResponse { 1360 | repeated LibraryUpdate update = 1; 1361 | } 1362 | message ClickLogEvent { 1363 | optional int64 eventTime = 1; 1364 | optional string url = 2; 1365 | optional string listId = 3; 1366 | optional string referrerUrl = 4; 1367 | optional string referrerListId = 5; 1368 | } 1369 | message ModifyLibraryRequest { 1370 | optional string libraryId = 1; 1371 | repeated string forAddDocid = 2; 1372 | repeated string forRemovalDocid = 3; 1373 | repeated string forArchiveDocid = 4; 1374 | } 1375 | message ModifyLibraryResponse { 1376 | optional LibraryUpdate libraryUpdate = 1; 1377 | } 1378 | message AndroidAppNotificationData { 1379 | optional int32 versionCode = 1; 1380 | optional string assetId = 2; 1381 | } 1382 | message InAppNotificationData { 1383 | optional string checkoutOrderId = 1; 1384 | optional string inAppNotificationId = 2; 1385 | } 1386 | message LibraryDirtyData { 1387 | optional int32 backend = 1; 1388 | optional string libraryId = 2; 1389 | } 1390 | message Notification { 1391 | optional int32 notificationType = 1; 1392 | optional int64 timestamp = 3; 1393 | optional Docid docid = 4; 1394 | optional string docTitle = 5; 1395 | optional string userEmail = 6; 1396 | optional AndroidAppNotificationData appData = 7; 1397 | optional AndroidAppDeliveryData appDeliveryData = 8; 1398 | optional PurchaseRemovalData purchaseRemovalData = 9; 1399 | optional UserNotificationData userNotificationData = 10; 1400 | optional InAppNotificationData inAppNotificationData = 11; 1401 | optional PurchaseDeclinedData purchaseDeclinedData = 12; 1402 | optional string notificationId = 13; 1403 | optional LibraryUpdate libraryUpdate = 14; 1404 | optional LibraryDirtyData libraryDirtyData = 15; 1405 | } 1406 | message PurchaseDeclinedData { 1407 | optional int32 reason = 1; 1408 | optional bool showNotification = 2; 1409 | } 1410 | message PurchaseRemovalData { 1411 | optional bool malicious = 1; 1412 | } 1413 | message UserNotificationData { 1414 | optional string notificationTitle = 1; 1415 | optional string notificationText = 2; 1416 | optional string tickerText = 3; 1417 | optional string dialogTitle = 4; 1418 | optional string dialogText = 5; 1419 | } 1420 | message OwnershipInfo { 1421 | optional int64 initiationTimestampMsec = 1; 1422 | optional int64 validUntilTimestampMsec = 2; 1423 | optional bool autoRenewing = 3; 1424 | optional int64 refundTimeoutTimestampMsec = 4; 1425 | optional int64 postDeliveryRefundWindowMsec = 5; 1426 | optional SignedData developerPurchaseInfo = 6; 1427 | optional bool preordered = 7; 1428 | optional bool hidden = 8; 1429 | optional RentalTerms rentalTerms = 9; 1430 | optional GroupLicenseInfo groupLicenseInfo = 10; 1431 | optional LicensedDocumentInfo licensedDocumentInfo = 11; 1432 | optional int32 quantity = 12; 1433 | optional int64 libraryExpirationTimestampMsec = 14; 1434 | } 1435 | message AppData { 1436 | optional int32 version = 1; 1437 | optional int32 oldVersion = 2; 1438 | optional bool systemApp = 3; 1439 | } 1440 | message PlayStoreBackgroundActionEvent { 1441 | optional int32 type = 1; 1442 | optional string document = 2; 1443 | optional string reason = 3; 1444 | optional int32 errorCode = 4; 1445 | optional string exceptionType = 5; 1446 | optional bytes serverLogsCookie = 6; 1447 | optional int32 offerType = 7; 1448 | optional int32 fromSetting = 8; 1449 | optional int32 toSetting = 9; 1450 | optional PlayStoreSessionData sessionInfo = 10; 1451 | optional AppData appData = 11; 1452 | optional int64 serverLatencyMs = 12; 1453 | optional int64 clientLatencyMs = 13; 1454 | } 1455 | message PlayStoreClickEvent { 1456 | repeated PlayStoreUiElement elementPath = 1; 1457 | } 1458 | message PlayStoreDeepLinkEvent { 1459 | optional string externalUrl = 1; 1460 | optional int32 resolvedType = 2; 1461 | } 1462 | message PlayStoreImpressionEvent { 1463 | optional PlayStoreUiElement tree = 1; 1464 | repeated PlayStoreUiElement referrerPath = 2; 1465 | optional int64 id = 3; 1466 | } 1467 | message PlayStoreLogEvent { 1468 | optional PlayStoreImpressionEvent impression = 1; 1469 | optional PlayStoreClickEvent click = 3; 1470 | optional PlayStoreBackgroundActionEvent backgroundAction = 4; 1471 | optional PlayStoreSearchEvent search = 5; 1472 | optional PlayStoreDeepLinkEvent deepLink = 6; 1473 | } 1474 | message PlayStoreSearchEvent { 1475 | optional string query = 1; 1476 | optional string queryUrl = 2; 1477 | optional string referrerUrl = 3; 1478 | } 1479 | message PlayStoreSessionData { 1480 | optional bool globalAutoUpdateEnabled = 1; 1481 | optional bool globalAutoUpdateOverWifiOnly = 2; 1482 | optional int32 autoUpdateCleanupDialogNumTimesShown = 3; 1483 | optional int32 networkType = 4; 1484 | optional int32 networkSubType = 5; 1485 | optional int32 numAccountsOnDevice = 6; 1486 | optional int32 numInstalledApps = 7; 1487 | optional int32 numAutoUpdatingInstalledApps = 8; 1488 | optional int32 numInstalledAppsNotAutoUpdating = 9; 1489 | optional bool gaiaPasswordAuthOptedOut = 10; 1490 | optional int32 contentFilterLevel = 11; 1491 | optional bool allowUnknownSources = 12; 1492 | } 1493 | message PlayStoreUiElement { 1494 | optional int32 type = 1; 1495 | optional bytes serverLogsCookie = 2; 1496 | optional PlayStoreUiElementInfo clientLogsCookie = 3; 1497 | repeated PlayStoreUiElement child = 4; 1498 | } 1499 | message PlayStoreUiElementInfo { 1500 | message InstrumentInfo { 1501 | optional int32 instrumentFamily = 1; 1502 | optional bool isDefault = 2; 1503 | } 1504 | optional InstrumentInfo instrumentInfo = 1; 1505 | optional string serialDocid = 2; 1506 | } 1507 | message PlusProfile { 1508 | optional string displayName = 2; 1509 | optional string profileImageUrl = 4; 1510 | optional Image profileImage = 5; 1511 | } 1512 | message PlusOneResponse { 1513 | } 1514 | message PlusProfileResponse { 1515 | optional PlusProfile plusProfile = 1; 1516 | } 1517 | message ClientCart { 1518 | optional string title = 1; 1519 | optional string formattedPrice = 2; 1520 | optional string purchaseContextToken = 3; 1521 | optional Instrument instrument = 4; 1522 | repeated string extendedDetailHtml = 5; 1523 | optional string footerHtml = 6; 1524 | optional string addInstrumentPromptHtml = 7; 1525 | optional string buttonText = 8; 1526 | optional Challenge completePurchaseChallenge = 9; 1527 | optional string priceByline = 10; 1528 | repeated string detailHtml = 11; 1529 | } 1530 | message CommitPurchaseResponse { 1531 | optional PurchaseStatus purchaseStatus = 1; 1532 | optional Challenge challenge = 2; 1533 | repeated LibraryUpdate libraryUpdate = 3; 1534 | optional AndroidAppDeliveryData appDeliveryData = 4; 1535 | optional bytes serverLogsCookie = 5; 1536 | } 1537 | message PreparePurchaseResponse { 1538 | optional PurchaseStatus purchaseStatus = 1; 1539 | optional Challenge challenge = 2; 1540 | optional ClientCart cart = 3; 1541 | repeated LibraryUpdate libraryUpdate = 4; 1542 | optional bytes serverLogsCookie = 5; 1543 | } 1544 | message PurchaseStatus { 1545 | optional int32 statusCode = 1; 1546 | optional string errorMessageHtml = 2; 1547 | optional int32 permissionError = 3; 1548 | } 1549 | message RateSuggestedContentResponse { 1550 | } 1551 | message AggregateRating { 1552 | optional int32 type = 1; 1553 | optional float starRating = 2; 1554 | optional uint64 ratingsCount = 3; 1555 | optional uint64 oneStarRatings = 4; 1556 | optional uint64 twoStarRatings = 5; 1557 | optional uint64 threeStarRatings = 6; 1558 | optional uint64 fourStarRatings = 7; 1559 | optional uint64 fiveStarRatings = 8; 1560 | optional uint64 thumbsUpCount = 9; 1561 | optional uint64 thumbsDownCount = 10; 1562 | optional uint64 commentCount = 11; 1563 | optional double bayesianMeanRating = 12; 1564 | } 1565 | message DirectPurchase { 1566 | optional string detailsUrl = 1; 1567 | optional string purchaseDocid = 2; 1568 | optional string parentDocid = 3; 1569 | optional int32 offerType = 4; 1570 | } 1571 | 1572 | message RedeemGiftCard { 1573 | optional string prefillCode = 1; 1574 | optional string partnerPayload = 2; 1575 | } 1576 | 1577 | message ResolvedLink { 1578 | optional string detailsUrl = 1; 1579 | optional string browseUrl = 2; 1580 | optional string searchUrl = 3; 1581 | optional DirectPurchase directPurchase = 4; 1582 | optional string homeUrl = 5; 1583 | optional RedeemGiftCard redeemGiftCard = 6; 1584 | optional bytes serverLogsCookie = 7; 1585 | optional Docid docid = 8; 1586 | optional string wishlistUrl = 9; 1587 | optional int32 backend = 10; 1588 | optional string query = 11; 1589 | } 1590 | message Payload { 1591 | optional ListResponse listResponse = 1; 1592 | optional DetailsResponse detailsResponse = 2; 1593 | optional ReviewResponse reviewResponse = 3; 1594 | optional BuyResponse buyResponse = 4; 1595 | optional SearchResponse searchResponse = 5; 1596 | optional TocResponse tocResponse = 6; 1597 | optional BrowseResponse browseResponse = 7; 1598 | optional PurchaseStatusResponse purchaseStatusResponse = 8; 1599 | optional UpdateInstrumentResponse updateInstrumentResponse = 9; 1600 | optional LogResponse logResponse = 10; 1601 | optional CheckInstrumentResponse checkInstrumentResponse = 11; 1602 | optional PlusOneResponse plusOneResponse = 12; 1603 | optional FlagContentResponse flagContentResponse = 13; 1604 | optional AckNotificationResponse ackNotificationResponse = 14; 1605 | optional InitiateAssociationResponse initiateAssociationResponse = 15; 1606 | optional VerifyAssociationResponse verifyAssociationResponse = 16; 1607 | optional LibraryReplicationResponse libraryReplicationResponse = 17; 1608 | optional RevokeResponse revokeResponse = 18; 1609 | optional BulkDetailsResponse bulkDetailsResponse = 19; 1610 | optional ResolvedLink resolveLinkResponse = 20; 1611 | optional DeliveryResponse deliveryResponse = 21; 1612 | optional AcceptTosResponse acceptTosResponse = 22; 1613 | optional RateSuggestedContentResponse rateSuggestedContentResponse = 23; 1614 | optional CheckPromoOfferResponse checkPromoOfferResponse = 24; 1615 | optional InstrumentSetupInfoResponse instrumentSetupInfoResponse = 25; 1616 | optional RedeemGiftCardResponse redeemGiftCardResponse = 26; 1617 | optional ModifyLibraryResponse modifyLibraryResponse = 27; 1618 | optional UploadDeviceConfigResponse uploadDeviceConfigResponse = 28; 1619 | optional PlusProfileResponse plusProfileResponse = 29; 1620 | optional ConsumePurchaseResponse consumePurchaseResponse = 30; 1621 | optional BillingProfileResponse billingProfileResponse = 31; 1622 | optional PreparePurchaseResponse preparePurchaseResponse = 32; 1623 | optional CommitPurchaseResponse commitPurchaseResponse = 33; 1624 | optional DebugSettingsResponse debugSettingsResponse = 34; 1625 | optional CheckIabPromoResponse checkIabPromoResponse = 35; 1626 | optional UserActivitySettingsResponse userActivitySettingsResponse = 36; 1627 | optional RecordUserActivityResponse recordUserActivityResponse = 37; 1628 | optional RedeemCodeResponse redeemCodeResponse = 38; 1629 | optional SelfUpdateResponse selfUpdateResponse = 39; 1630 | optional searchSuggestResponse searchSuggestResponse = 40; 1631 | optional GetInitialInstrumentFlowStateResponse getInitialInstrumentFlowStateResponse = 41; 1632 | optional CreateInstrumentResponse createInstrumentResponse = 42; 1633 | optional ChallengeResponse challengeResponse = 43; 1634 | optional BackDeviceChoicesResponse backupDeviceChoicesResponse = 44; 1635 | optional BackupDocumentChoicesResponse backupDocumentChoicesResponse = 45; 1636 | optional EarlyUpdateResponse earlyUpdateResponse = 46; 1637 | optional PreloadsResponse preloadsResponse = 47; 1638 | optional MyAccountsResponse myAccountsResponse = 48; 1639 | optional ContentFilterResponse contentFilterResponse = 49; 1640 | optional ExperimentsResponse experimentsResponse = 50; 1641 | optional SurveyResponse surveyResponse = 51; 1642 | optional PingResponse pingResponse = 52; 1643 | optional UpdateUserSettingResponse updateUserSettingResponse = 53; 1644 | optional GetUserSettingsResponse getUserSettingsREsponse = 54; 1645 | optional GetSharingSettingsResponse getSharingSettingsResponse = 56; 1646 | optional UpdateSharingSettingsResponse updateSharingSettingsResponse = 57; 1647 | optional ReviewSnippetsResponse reviewSnippetsResponse = 58; 1648 | optional DocumentSharingStateResponse documentSharingStateResponse = 59; 1649 | optional ModuleDeliveryResponse moduleDeliveryResponse = 70; 1650 | } 1651 | 1652 | message CheckIabPromoResponse {} 1653 | message UserActivitySettingsResponse {} 1654 | message RecordUserActivityResponse {} 1655 | message RedeemCodeResponse {} 1656 | message SelfUpdateResponse {} 1657 | message searchSuggestResponse {} 1658 | message GetInitialInstrumentFlowStateResponse {} 1659 | message CreateInstrumentResponse {} 1660 | message ChallengeResponse {} 1661 | message BackDeviceChoicesResponse {} 1662 | message BackupDocumentChoicesResponse {} 1663 | message EarlyUpdateResponse {} 1664 | message PreloadsResponse {} 1665 | message MyAccountsResponse {} 1666 | message ContentFilterResponse {} 1667 | message ExperimentsResponse {} 1668 | message SurveyResponse {} 1669 | message PingResponse {} 1670 | message UpdateUserSettingResponse {} 1671 | message GetUserSettingsResponse {} 1672 | message GetSharingSettingsResponse {} 1673 | message UpdateSharingSettingsResponse {} 1674 | message ReviewSnippetsResponse {} 1675 | message DocumentSharingStateResponse {} 1676 | message ModuleDeliveryResponse {} 1677 | 1678 | message PreFetch { 1679 | optional string url = 1; 1680 | optional bytes response = 2; 1681 | optional string etag = 3; 1682 | optional int64 ttl = 4; 1683 | optional int64 softTtl = 5; 1684 | } 1685 | message ResponseWrapper { 1686 | optional Payload payload = 1; 1687 | optional ServerCommands commands = 2; 1688 | repeated PreFetch preFetch = 3; 1689 | repeated Notification notification = 4; 1690 | optional ServerMetadata serverMetadata = 5; 1691 | } 1692 | message ServerCommands { 1693 | optional bool clearCache = 1; 1694 | optional string displayErrorMessage = 2; 1695 | optional string logErrorStacktrace = 3; 1696 | } 1697 | message ServerMetadata { 1698 | optional int64 latencyMillis = 1; 1699 | } 1700 | message GetReviewsResponse { 1701 | repeated Review review = 1; 1702 | optional int64 matchingCount = 2; 1703 | } 1704 | message Review { 1705 | optional string authorName = 1; 1706 | optional string url = 2; 1707 | optional string source = 3; 1708 | optional string documentVersion = 4; 1709 | optional int64 timestampMsec = 5; 1710 | optional int32 starRating = 6; 1711 | optional string title = 7; 1712 | optional string comment = 8; 1713 | optional string commentId = 9; 1714 | optional string deviceName = 19; 1715 | optional string replyText = 29; 1716 | optional int64 replyTimestampMsec = 30; 1717 | optional PlusProfile plusProfile = 31; 1718 | optional DocV2 author = 33; 1719 | optional Image sentiment = 34; 1720 | } 1721 | 1722 | message ReviewResponse { 1723 | optional GetReviewsResponse getResponse = 1; 1724 | optional string nextPageUrl = 2; 1725 | optional Review updatedReview = 3; 1726 | } 1727 | message RevokeResponse { 1728 | optional LibraryUpdate libraryUpdate = 1; 1729 | } 1730 | message RelatedSearch { 1731 | optional string searchUrl = 1; 1732 | optional string header = 2; 1733 | optional int32 backendId = 3; 1734 | optional int32 docType = 4; 1735 | optional bool current = 5; 1736 | } 1737 | message SearchResponse { 1738 | optional string originalQuery = 1; 1739 | optional string suggestedQuery = 2; 1740 | optional bool aggregateQuery = 3; 1741 | repeated Bucket bucket = 4; 1742 | repeated DocV2 doc = 5; 1743 | repeated RelatedSearch relatedSearch = 6; 1744 | optional bytes serverLogsCookie = 7; 1745 | } 1746 | message BillingConfig { 1747 | optional CarrierBillingConfig carrierBillingConfig = 1; 1748 | optional int32 maxIabApiVersion = 2; 1749 | } 1750 | message CarrierBillingConfig { 1751 | optional string id = 1; 1752 | optional string name = 2; 1753 | optional int32 apiVersion = 3; 1754 | optional string provisioningUrl = 4; 1755 | optional string credentialsUrl = 5; 1756 | optional bool tosRequired = 6; 1757 | optional bool perTransactionCredentialsRequired = 7; 1758 | optional bool sendSubscriberIdWithCarrierBillingRequests = 8; 1759 | } 1760 | message CorpusMetadata { 1761 | optional int32 backend = 1; 1762 | optional string name = 2; 1763 | optional string landingUrl = 3; 1764 | optional string libraryName = 4; 1765 | optional string recsWidgetUrl = 6; 1766 | } 1767 | message Experiments { 1768 | repeated string experimentId = 1; 1769 | } 1770 | message SelfUpdateConfig { 1771 | optional int32 latestClientVersionCode = 1; 1772 | } 1773 | message TocResponse { 1774 | repeated CorpusMetadata corpus = 1; 1775 | optional int32 tosVersionDeprecated = 2; 1776 | optional string tosContent = 3; 1777 | optional string homeUrl = 4; 1778 | optional Experiments experiments = 5; 1779 | optional string tosCheckboxTextMarketingEmails = 6; 1780 | optional string tosToken = 7; 1781 | optional UserSettings userSettings = 8; 1782 | optional string iconOverrideUrl = 9; 1783 | optional SelfUpdateConfig selfUpdateConfig = 10; 1784 | optional bool requiresUploadDeviceConfig = 11; 1785 | optional BillingConfig billingConfig = 12; 1786 | optional string recsWidgetUrl = 13; 1787 | } 1788 | message UserSettings { 1789 | optional bool tosCheckboxMarketingEmailsOptedIn = 1; 1790 | } 1791 | message AcceptTosResponse { 1792 | } 1793 | message UploadDeviceConfigRequest { 1794 | optional DeviceConfigurationProto deviceConfiguration = 1; 1795 | optional string manufacturer = 2; 1796 | optional string gcmRegistrationId = 3; 1797 | } 1798 | message UploadDeviceConfigResponse { 1799 | optional string uploadDeviceConfigToken = 1; 1800 | } 1801 | message AckNotificationsRequestProto { 1802 | repeated string notificationId = 1; 1803 | optional SignatureHashProto signatureHash = 2; 1804 | repeated string nackNotificationId = 3; 1805 | } 1806 | message AckNotificationsResponseProto { 1807 | } 1808 | message AddressProto { 1809 | optional string address1 = 1; 1810 | optional string address2 = 2; 1811 | optional string city = 3; 1812 | optional string state = 4; 1813 | optional string postalCode = 5; 1814 | optional string country = 6; 1815 | optional string name = 7; 1816 | optional string type = 8; 1817 | optional string phone = 9; 1818 | } 1819 | message AppDataProto { 1820 | optional string key = 1; 1821 | optional string value = 2; 1822 | } 1823 | message AppSuggestionProto { 1824 | optional ExternalAssetProto assetInfo = 1; 1825 | } 1826 | message AssetIdentifierProto { 1827 | optional string packageName = 1; 1828 | optional int32 versionCode = 2; 1829 | optional string assetId = 3; 1830 | } 1831 | message AssetsRequestProto { 1832 | optional int32 assetType = 1; 1833 | optional string query = 2; 1834 | optional string categoryId = 3; 1835 | repeated string assetId = 4; 1836 | optional bool retrieveVendingHistory = 5; 1837 | optional bool retrieveExtendedInfo = 6; 1838 | optional int32 sortOrder = 7; 1839 | optional int64 startIndex = 8; 1840 | optional int64 numEntries = 9; 1841 | optional int32 viewFilter = 10; 1842 | optional string rankingType = 11; 1843 | optional bool retrieveCarrierChannel = 12; 1844 | repeated string pendingDownloadAssetId = 13; 1845 | optional bool reconstructVendingHistory = 14; 1846 | optional bool unfilteredResults = 15; 1847 | repeated string badgeId = 16; 1848 | } 1849 | message AssetsResponseProto { 1850 | repeated ExternalAssetProto asset = 1; 1851 | optional int64 numTotalEntries = 2; 1852 | optional string correctedQuery = 3; 1853 | repeated ExternalAssetProto altAsset = 4; 1854 | optional int64 numCorrectedEntries = 5; 1855 | optional string header = 6; 1856 | optional int32 listType = 7; 1857 | } 1858 | message BillingEventRequestProto { 1859 | optional int32 eventType = 1; 1860 | optional string billingParametersId = 2; 1861 | optional bool resultSuccess = 3; 1862 | optional string clientMessage = 4; 1863 | optional ExternalCarrierBillingInstrumentProto carrierInstrument = 5; 1864 | } 1865 | message BillingEventResponseProto { 1866 | } 1867 | message BillingParameterProto { 1868 | optional string id = 1; 1869 | optional string name = 2; 1870 | repeated string mncMcc = 3; 1871 | repeated string backendUrl = 4; 1872 | optional string iconId = 5; 1873 | optional int32 billingInstrumentType = 6; 1874 | optional string applicationId = 7; 1875 | optional string tosUrl = 8; 1876 | optional bool instrumentTosRequired = 9; 1877 | optional int32 apiVersion = 10; 1878 | optional bool perTransactionCredentialsRequired = 11; 1879 | optional bool sendSubscriberIdWithCarrierBillingRequests = 12; 1880 | optional int32 deviceAssociationMethod = 13; 1881 | optional string userTokenRequestMessage = 14; 1882 | optional string userTokenRequestAddress = 15; 1883 | optional bool passphraseRequired = 16; 1884 | } 1885 | message CarrierBillingCredentialsProto { 1886 | optional string credentials = 1; 1887 | optional int64 credentialsTimeout = 2; 1888 | } 1889 | message CategoryProto { 1890 | optional int32 assetType = 2; 1891 | optional string categoryId = 3; 1892 | optional string categoryDisplay = 4; 1893 | optional string categorySubtitle = 5; 1894 | repeated string promotedAssetsNew = 6; 1895 | repeated string promotedAssetsHome = 7; 1896 | repeated CategoryProto subCategories = 8; 1897 | repeated string promotedAssetsPaid = 9; 1898 | repeated string promotedAssetsFree = 10; 1899 | } 1900 | message CheckForNotificationsRequestProto { 1901 | optional int64 alarmDuration = 1; 1902 | } 1903 | message CheckForNotificationsResponseProto { 1904 | } 1905 | message CheckLicenseRequestProto { 1906 | optional string packageName = 1; 1907 | optional int32 versionCode = 2; 1908 | optional int64 nonce = 3; 1909 | } 1910 | message CheckLicenseResponseProto { 1911 | optional int32 responseCode = 1; 1912 | optional string signedData = 2; 1913 | optional string signature = 3; 1914 | } 1915 | message CommentsRequestProto { 1916 | optional string assetId = 1; 1917 | optional int64 startIndex = 2; 1918 | optional int64 numEntries = 3; 1919 | optional bool shouldReturnSelfComment = 4; 1920 | optional string assetReferrer = 5; 1921 | } 1922 | message CommentsResponseProto { 1923 | repeated ExternalCommentProto comment = 1; 1924 | optional int64 numTotalEntries = 2; 1925 | optional ExternalCommentProto selfComment = 3; 1926 | } 1927 | message ContentSyncRequestProto { 1928 | optional bool incremental = 1; 1929 | repeated group AssetInstallState = 2 { 1930 | optional string assetId = 3; 1931 | optional int32 assetState = 4; 1932 | optional int64 installTime = 5; 1933 | optional int64 uninstallTime = 6; 1934 | optional string packageName = 7; 1935 | optional int32 versionCode = 8; 1936 | optional string assetReferrer = 9; 1937 | } 1938 | repeated group SystemApp = 10 { 1939 | optional string packageName = 11; 1940 | optional int32 versionCode = 12; 1941 | repeated string certificateHash = 13; 1942 | } 1943 | optional int32 sideloadedAppCount = 14; 1944 | } 1945 | message ContentSyncResponseProto { 1946 | optional int32 numUpdatesAvailable = 1; 1947 | } 1948 | message DataMessageProto { 1949 | optional string category = 1; 1950 | repeated AppDataProto appData = 3; 1951 | } 1952 | message DownloadInfoProto { 1953 | optional int64 apkSize = 1; 1954 | repeated FileMetadataProto additionalFile = 2; 1955 | } 1956 | message ExternalAssetProto { 1957 | optional string id = 1; 1958 | optional string title = 2; 1959 | optional int32 assetType = 3; 1960 | optional string owner = 4; 1961 | optional string version = 5; 1962 | optional string price = 6; 1963 | optional string averageRating = 7; 1964 | optional int64 numRatings = 8; 1965 | optional group PurchaseInformation = 9 { 1966 | optional int64 purchaseTime = 10; 1967 | optional int64 refundTimeoutTime = 11; 1968 | optional int32 refundStartPolicy = 45; 1969 | optional int64 refundWindowDuration = 46; 1970 | } 1971 | optional group ExtendedInfo = 12 { 1972 | optional string description = 13; 1973 | optional int64 downloadCount = 14; 1974 | repeated string applicationPermissionId = 15; 1975 | optional int64 requiredInstallationSize = 16; 1976 | optional string packageName = 17; 1977 | optional string category = 18; 1978 | optional bool forwardLocked = 19; 1979 | optional string contactEmail = 20; 1980 | optional bool everInstalledByUser = 21; 1981 | optional string downloadCountString = 23; 1982 | optional string contactPhone = 26; 1983 | optional string contactWebsite = 27; 1984 | optional bool nextPurchaseRefundable = 28; 1985 | optional int32 numScreenshots = 30; 1986 | optional string promotionalDescription = 31; 1987 | optional int32 serverAssetState = 34; 1988 | optional int32 contentRatingLevel = 36; 1989 | optional string contentRatingString = 37; 1990 | optional string recentChanges = 38; 1991 | repeated group PackageDependency = 39 { 1992 | optional string packageName = 41; 1993 | optional bool skipPermissions = 42; 1994 | } 1995 | optional string videoLink = 43; 1996 | optional DownloadInfoProto downloadInfo = 49; 1997 | } 1998 | optional string ownerId = 22; 1999 | optional string packageName = 24; 2000 | optional int32 versionCode = 25; 2001 | optional bool bundledAsset = 29; 2002 | optional string priceCurrency = 32; 2003 | optional int64 priceMicros = 33; 2004 | optional string filterReason = 35; 2005 | optional string actualSellerPrice = 40; 2006 | repeated ExternalBadgeProto appBadge = 47; 2007 | repeated ExternalBadgeProto ownerBadge = 48; 2008 | } 2009 | message ExternalBadgeImageProto { 2010 | optional int32 usage = 1; 2011 | optional string url = 2; 2012 | } 2013 | message ExternalBadgeProto { 2014 | optional string localizedTitle = 1; 2015 | optional string localizedDescription = 2; 2016 | repeated ExternalBadgeImageProto badgeImage = 3; 2017 | optional string searchId = 4; 2018 | } 2019 | message ExternalCarrierBillingInstrumentProto { 2020 | optional string instrumentKey = 1; 2021 | optional string subscriberIdentifier = 2; 2022 | optional string accountType = 3; 2023 | optional string subscriberCurrency = 4; 2024 | optional uint64 transactionLimit = 5; 2025 | optional string subscriberName = 6; 2026 | optional string address1 = 7; 2027 | optional string address2 = 8; 2028 | optional string city = 9; 2029 | optional string state = 10; 2030 | optional string postalCode = 11; 2031 | optional string country = 12; 2032 | optional EncryptedSubscriberInfo encryptedSubscriberInfo = 13; 2033 | } 2034 | message ExternalCommentProto { 2035 | optional string body = 1; 2036 | optional int32 rating = 2; 2037 | optional string creatorName = 3; 2038 | optional int64 creationTime = 4; 2039 | optional string creatorId = 5; 2040 | } 2041 | message ExternalCreditCard { 2042 | optional string type = 1; 2043 | optional string lastDigits = 2; 2044 | optional int32 expYear = 3; 2045 | optional int32 expMonth = 4; 2046 | optional string personName = 5; 2047 | optional string countryCode = 6; 2048 | optional string postalCode = 7; 2049 | optional bool makeDefault = 8; 2050 | optional string address1 = 9; 2051 | optional string address2 = 10; 2052 | optional string city = 11; 2053 | optional string state = 12; 2054 | optional string phone = 13; 2055 | } 2056 | message ExternalPaypalInstrumentProto { 2057 | optional string instrumentKey = 1; 2058 | optional string preapprovalKey = 2; 2059 | optional string paypalEmail = 3; 2060 | optional AddressProto paypalAddress = 4; 2061 | optional bool multiplePaypalInstrumentsSupported = 5; 2062 | } 2063 | message FileMetadataProto { 2064 | optional int32 fileType = 1; 2065 | optional int32 versionCode = 2; 2066 | optional int64 size = 3; 2067 | optional string downloadUrl = 4; 2068 | } 2069 | message GetAddressSnippetRequestProto { 2070 | optional EncryptedSubscriberInfo encryptedSubscriberInfo = 1; 2071 | } 2072 | message GetAddressSnippetResponseProto { 2073 | optional string addressSnippet = 1; 2074 | } 2075 | message GetAssetRequestProto { 2076 | optional string assetId = 1; 2077 | optional string directDownloadKey = 2; 2078 | } 2079 | message GetAssetResponseProto { 2080 | optional group InstallAsset = 1 { 2081 | optional string assetId = 2; 2082 | optional string assetName = 3; 2083 | optional string assetType = 4; 2084 | optional string assetPackage = 5; 2085 | optional string blobUrl = 6; 2086 | optional string assetSignature = 7; 2087 | optional int64 assetSize = 8; 2088 | optional int64 refundTimeoutMillis = 9; 2089 | optional bool forwardLocked = 10; 2090 | optional bool secured = 11; 2091 | optional int32 versionCode = 12; 2092 | optional string downloadAuthCookieName = 13; 2093 | optional string downloadAuthCookieValue = 14; 2094 | optional int64 postInstallRefundWindowMillis = 16; 2095 | } 2096 | repeated FileMetadataProto additionalFile = 15; 2097 | } 2098 | message GetCarrierInfoRequestProto { 2099 | } 2100 | message GetCarrierInfoResponseProto { 2101 | optional bool carrierChannelEnabled = 1; 2102 | optional bytes carrierLogoIcon = 2; 2103 | optional bytes carrierBanner = 3; 2104 | optional string carrierSubtitle = 4; 2105 | optional string carrierTitle = 5; 2106 | optional int32 carrierImageDensity = 6; 2107 | } 2108 | message GetCategoriesRequestProto { 2109 | optional bool prefetchPromoData = 1; 2110 | } 2111 | message GetCategoriesResponseProto { 2112 | repeated CategoryProto categories = 1; 2113 | } 2114 | message GetImageRequestProto { 2115 | optional string assetId = 1; 2116 | optional int32 imageUsage = 3; 2117 | optional string imageId = 4; 2118 | optional int32 screenPropertyWidth = 5; 2119 | optional int32 screenPropertyHeight = 6; 2120 | optional int32 screenPropertyDensity = 7; 2121 | optional int32 productType = 8; 2122 | } 2123 | message GetImageResponseProto { 2124 | optional bytes imageData = 1; 2125 | optional int32 imageDensity = 2; 2126 | } 2127 | message GetMarketMetadataRequestProto { 2128 | optional int64 lastRequestTime = 1; 2129 | optional DeviceConfigurationProto deviceConfiguration = 2; 2130 | optional bool deviceRoaming = 3; 2131 | repeated string marketSignatureHash = 4; 2132 | optional int32 contentRating = 5; 2133 | optional string deviceModelName = 6; 2134 | optional string deviceManufacturerName = 7; 2135 | } 2136 | message GetMarketMetadataResponseProto { 2137 | optional int32 latestClientVersionCode = 1; 2138 | optional string latestClientUrl = 2; 2139 | optional bool paidAppsEnabled = 3; 2140 | repeated BillingParameterProto billingParameter = 4; 2141 | optional bool commentPostEnabled = 5; 2142 | optional bool billingEventsEnabled = 6; 2143 | optional string warningMessage = 7; 2144 | optional bool inAppBillingEnabled = 8; 2145 | optional int32 inAppBillingMaxApiVersion = 9; 2146 | } 2147 | message GetSubCategoriesRequestProto { 2148 | optional int32 assetType = 1; 2149 | } 2150 | message GetSubCategoriesResponseProto { 2151 | repeated group SubCategory = 1 { 2152 | optional string subCategoryDisplay = 2; 2153 | optional string subCategoryId = 3; 2154 | } 2155 | } 2156 | message InAppPurchaseInformationRequestProto { 2157 | optional SignatureHashProto signatureHash = 1; 2158 | optional int64 nonce = 2; 2159 | repeated string notificationId = 3; 2160 | optional string signatureAlgorithm = 4; 2161 | optional int32 billingApiVersion = 5; 2162 | } 2163 | message InAppPurchaseInformationResponseProto { 2164 | optional SignedDataProto signedResponse = 1; 2165 | repeated StatusBarNotificationProto statusBarNotification = 2; 2166 | optional PurchaseResultProto purchaseResult = 3; 2167 | } 2168 | message InAppRestoreTransactionsRequestProto { 2169 | optional SignatureHashProto signatureHash = 1; 2170 | optional int64 nonce = 2; 2171 | optional string signatureAlgorithm = 3; 2172 | optional int32 billingApiVersion = 4; 2173 | } 2174 | message InAppRestoreTransactionsResponseProto { 2175 | optional SignedDataProto signedResponse = 1; 2176 | optional PurchaseResultProto purchaseResult = 2; 2177 | } 2178 | message ModifyCommentRequestProto { 2179 | optional string assetId = 1; 2180 | optional ExternalCommentProto comment = 2; 2181 | optional bool deleteComment = 3; 2182 | optional bool flagAsset = 4; 2183 | optional int32 flagType = 5; 2184 | optional string flagMessage = 6; 2185 | optional bool nonFlagFlow = 7; 2186 | } 2187 | message ModifyCommentResponseProto { 2188 | } 2189 | message PaypalCountryInfoProto { 2190 | optional bool birthDateRequired = 1; 2191 | optional string tosText = 2; 2192 | optional string billingAgreementText = 3; 2193 | optional string preTosText = 4; 2194 | } 2195 | message PaypalCreateAccountRequestProto { 2196 | optional string firstName = 1; 2197 | optional string lastName = 2; 2198 | optional AddressProto address = 3; 2199 | optional string birthDate = 4; 2200 | } 2201 | message PaypalCreateAccountResponseProto { 2202 | optional string createAccountKey = 1; 2203 | } 2204 | message PaypalCredentialsProto { 2205 | optional string preapprovalKey = 1; 2206 | optional string paypalEmail = 2; 2207 | } 2208 | message PaypalMassageAddressRequestProto { 2209 | optional AddressProto address = 1; 2210 | } 2211 | message PaypalMassageAddressResponseProto { 2212 | optional AddressProto address = 1; 2213 | } 2214 | message PaypalPreapprovalCredentialsRequestProto { 2215 | optional string gaiaAuthToken = 1; 2216 | optional string billingInstrumentId = 2; 2217 | } 2218 | message PaypalPreapprovalCredentialsResponseProto { 2219 | optional int32 resultCode = 1; 2220 | optional string paypalAccountKey = 2; 2221 | optional string paypalEmail = 3; 2222 | } 2223 | message PaypalPreapprovalDetailsRequestProto { 2224 | optional bool getAddress = 1; 2225 | optional string preapprovalKey = 2; 2226 | } 2227 | message PaypalPreapprovalDetailsResponseProto { 2228 | optional string paypalEmail = 1; 2229 | optional AddressProto address = 2; 2230 | } 2231 | message PaypalPreapprovalRequestProto { 2232 | } 2233 | message PaypalPreapprovalResponseProto { 2234 | optional string preapprovalKey = 1; 2235 | } 2236 | message PendingNotificationsProto { 2237 | repeated DataMessageProto notification = 1; 2238 | optional int64 nextCheckMillis = 2; 2239 | } 2240 | message PrefetchedBundleProto { 2241 | optional SingleRequestProto request = 1; 2242 | optional SingleResponseProto response = 2; 2243 | } 2244 | message PurchaseCartInfoProto { 2245 | optional string itemPrice = 1; 2246 | optional string taxInclusive = 2; 2247 | optional string taxExclusive = 3; 2248 | optional string total = 4; 2249 | optional string taxMessage = 5; 2250 | optional string footerMessage = 6; 2251 | optional string priceCurrency = 7; 2252 | optional int64 priceMicros = 8; 2253 | } 2254 | message PurchaseInfoProto { 2255 | optional string transactionId = 1; 2256 | optional PurchaseCartInfoProto cartInfo = 2; 2257 | optional group BillingInstruments = 3 { 2258 | repeated group BillingInstrument = 4 { 2259 | optional string id = 5; 2260 | optional string name = 6; 2261 | optional bool isInvalid = 7; 2262 | optional int32 instrumentType = 11; 2263 | optional int32 instrumentStatus = 14; 2264 | } 2265 | optional string defaultBillingInstrumentId = 8; 2266 | } 2267 | repeated int32 errorInputFields = 9; 2268 | optional string refundPolicy = 10; 2269 | optional bool userCanAddGdd = 12; 2270 | repeated int32 eligibleInstrumentTypes = 13; 2271 | optional string orderId = 15; 2272 | } 2273 | message PurchaseMetadataRequestProto { 2274 | optional bool deprecatedRetrieveBillingCountries = 1; 2275 | optional int32 billingInstrumentType = 2; 2276 | } 2277 | message PurchaseMetadataResponseProto { 2278 | optional group Countries = 1 { 2279 | repeated group Country = 2 { 2280 | optional string countryCode = 3; 2281 | optional string countryName = 4; 2282 | optional PaypalCountryInfoProto paypalCountryInfo = 5; 2283 | optional bool allowsReducedBillingAddress = 6; 2284 | repeated group InstrumentAddressSpec = 7 { 2285 | optional int32 instrumentFamily = 8; 2286 | optional BillingAddressSpec billingAddressSpec = 9; 2287 | } 2288 | } 2289 | } 2290 | } 2291 | message PurchaseOrderRequestProto { 2292 | optional string gaiaAuthToken = 1; 2293 | optional string assetId = 2; 2294 | optional string transactionId = 3; 2295 | optional string billingInstrumentId = 4; 2296 | optional bool tosAccepted = 5; 2297 | optional CarrierBillingCredentialsProto carrierBillingCredentials = 6; 2298 | optional string existingOrderId = 7; 2299 | optional int32 billingInstrumentType = 8; 2300 | optional string billingParametersId = 9; 2301 | optional PaypalCredentialsProto paypalCredentials = 10; 2302 | optional RiskHeaderInfoProto riskHeaderInfo = 11; 2303 | optional int32 productType = 12; 2304 | optional SignatureHashProto signatureHash = 13; 2305 | optional string developerPayload = 14; 2306 | } 2307 | message PurchaseOrderResponseProto { 2308 | optional int32 deprecatedResultCode = 1; 2309 | optional PurchaseInfoProto purchaseInfo = 2; 2310 | optional ExternalAssetProto asset = 3; 2311 | optional PurchaseResultProto purchaseResult = 4; 2312 | } 2313 | message PurchasePostRequestProto { 2314 | optional string gaiaAuthToken = 1; 2315 | optional string assetId = 2; 2316 | optional string transactionId = 3; 2317 | optional group BillingInstrumentInfo = 4 { 2318 | optional string billingInstrumentId = 5; 2319 | optional ExternalCreditCard creditCard = 6; 2320 | optional ExternalCarrierBillingInstrumentProto carrierInstrument = 9; 2321 | optional ExternalPaypalInstrumentProto paypalInstrument = 10; 2322 | } 2323 | optional bool tosAccepted = 7; 2324 | optional string cbInstrumentKey = 8; 2325 | optional bool paypalAuthConfirmed = 11; 2326 | optional int32 productType = 12; 2327 | optional SignatureHashProto signatureHash = 13; 2328 | } 2329 | message PurchasePostResponseProto { 2330 | optional int32 deprecatedResultCode = 1; 2331 | optional PurchaseInfoProto purchaseInfo = 2; 2332 | optional string termsOfServiceUrl = 3; 2333 | optional string termsOfServiceText = 4; 2334 | optional string termsOfServiceName = 5; 2335 | optional string termsOfServiceCheckboxText = 6; 2336 | optional string termsOfServiceHeaderText = 7; 2337 | optional PurchaseResultProto purchaseResult = 8; 2338 | } 2339 | message PurchaseProductRequestProto { 2340 | optional int32 productType = 1; 2341 | optional string productId = 2; 2342 | optional SignatureHashProto signatureHash = 3; 2343 | } 2344 | message PurchaseProductResponseProto { 2345 | optional string title = 1; 2346 | optional string itemTitle = 2; 2347 | optional string itemDescription = 3; 2348 | optional string merchantField = 4; 2349 | } 2350 | message PurchaseResultProto { 2351 | optional int32 resultCode = 1; 2352 | optional string resultCodeMessage = 2; 2353 | } 2354 | message QuerySuggestionProto { 2355 | optional string query = 1; 2356 | optional int32 estimatedNumResults = 2; 2357 | optional int32 queryWeight = 3; 2358 | } 2359 | message QuerySuggestionRequestProto { 2360 | optional string query = 1; 2361 | optional int32 requestType = 2; 2362 | } 2363 | message QuerySuggestionResponseProto { 2364 | repeated group Suggestion = 1 { 2365 | optional AppSuggestionProto appSuggestion = 2; 2366 | optional QuerySuggestionProto querySuggestion = 3; 2367 | } 2368 | optional int32 estimatedNumAppSuggestions = 4; 2369 | optional int32 estimatedNumQuerySuggestions = 5; 2370 | } 2371 | message RateCommentRequestProto { 2372 | optional string assetId = 1; 2373 | optional string creatorId = 2; 2374 | optional int32 commentRating = 3; 2375 | } 2376 | message RateCommentResponseProto { 2377 | } 2378 | message ReconstructDatabaseRequestProto { 2379 | optional bool retrieveFullHistory = 1; 2380 | } 2381 | message ReconstructDatabaseResponseProto { 2382 | repeated AssetIdentifierProto asset = 1; 2383 | } 2384 | message RefundRequestProto { 2385 | optional string assetId = 1; 2386 | } 2387 | message RefundResponseProto { 2388 | optional int32 result = 1; 2389 | optional ExternalAssetProto asset = 2; 2390 | optional string resultDetail = 3; 2391 | } 2392 | message RemoveAssetRequestProto { 2393 | optional string assetId = 1; 2394 | } 2395 | message RequestPropertiesProto { 2396 | optional string userAuthToken = 1; 2397 | optional bool userAuthTokenSecure = 2; 2398 | optional int32 softwareVersion = 3; 2399 | optional string aid = 4; 2400 | optional string productNameAndVersion = 5; 2401 | optional string userLanguage = 6; 2402 | optional string userCountry = 7; 2403 | optional string operatorName = 8; 2404 | optional string simOperatorName = 9; 2405 | optional string operatorNumericName = 10; 2406 | optional string simOperatorNumericName = 11; 2407 | optional string clientId = 12; 2408 | optional string loggingId = 13; 2409 | } 2410 | message RequestProto { 2411 | optional RequestPropertiesProto requestProperties = 1; 2412 | repeated group Request = 2 { 2413 | optional RequestSpecificPropertiesProto requestSpecificProperties = 3; 2414 | optional AssetsRequestProto assetRequest = 4; 2415 | optional CommentsRequestProto commentsRequest = 5; 2416 | optional ModifyCommentRequestProto modifyCommentRequest = 6; 2417 | optional PurchasePostRequestProto purchasePostRequest = 7; 2418 | optional PurchaseOrderRequestProto purchaseOrderRequest = 8; 2419 | optional ContentSyncRequestProto contentSyncRequest = 9; 2420 | optional GetAssetRequestProto getAssetRequest = 10; 2421 | optional GetImageRequestProto getImageRequest = 11; 2422 | optional RefundRequestProto refundRequest = 12; 2423 | optional PurchaseMetadataRequestProto purchaseMetadataRequest = 13; 2424 | optional GetSubCategoriesRequestProto subCategoriesRequest = 14; 2425 | optional UninstallReasonRequestProto uninstallReasonRequest = 16; 2426 | optional RateCommentRequestProto rateCommentRequest = 17; 2427 | optional CheckLicenseRequestProto checkLicenseRequest = 18; 2428 | optional GetMarketMetadataRequestProto getMarketMetadataRequest = 19; 2429 | optional GetCategoriesRequestProto getCategoriesRequest = 21; 2430 | optional GetCarrierInfoRequestProto getCarrierInfoRequest = 22; 2431 | optional RemoveAssetRequestProto removeAssetRequest = 23; 2432 | optional RestoreApplicationsRequestProto restoreApplicationsRequest = 24; 2433 | optional QuerySuggestionRequestProto querySuggestionRequest = 25; 2434 | optional BillingEventRequestProto billingEventRequest = 26; 2435 | optional PaypalPreapprovalRequestProto paypalPreapprovalRequest = 27; 2436 | optional PaypalPreapprovalDetailsRequestProto paypalPreapprovalDetailsRequest = 28; 2437 | optional PaypalCreateAccountRequestProto paypalCreateAccountRequest = 29; 2438 | optional PaypalPreapprovalCredentialsRequestProto paypalPreapprovalCredentialsRequest = 30; 2439 | optional InAppRestoreTransactionsRequestProto inAppRestoreTransactionsRequest = 31; 2440 | optional InAppPurchaseInformationRequestProto inAppPurchaseInformationRequest = 32; 2441 | optional CheckForNotificationsRequestProto checkForNotificationsRequest = 33; 2442 | optional AckNotificationsRequestProto ackNotificationsRequest = 34; 2443 | optional PurchaseProductRequestProto purchaseProductRequest = 35; 2444 | optional ReconstructDatabaseRequestProto reconstructDatabaseRequest = 36; 2445 | optional PaypalMassageAddressRequestProto paypalMassageAddressRequest = 37; 2446 | optional GetAddressSnippetRequestProto getAddressSnippetRequest = 38; 2447 | } 2448 | } 2449 | message RequestSpecificPropertiesProto { 2450 | optional string ifNoneMatch = 1; 2451 | } 2452 | message ResponsePropertiesProto { 2453 | optional int32 result = 1; 2454 | optional int32 maxAge = 2; 2455 | optional string etag = 3; 2456 | optional int32 serverVersion = 4; 2457 | optional int32 maxAgeConsumable = 6; 2458 | optional string errorMessage = 7; 2459 | repeated InputValidationError errorInputField = 8; 2460 | } 2461 | message ResponseProto { 2462 | repeated group Response = 1 { 2463 | optional ResponsePropertiesProto responseProperties = 2; 2464 | optional AssetsResponseProto assetsResponse = 3; 2465 | optional CommentsResponseProto commentsResponse = 4; 2466 | optional ModifyCommentResponseProto modifyCommentResponse = 5; 2467 | optional PurchasePostResponseProto purchasePostResponse = 6; 2468 | optional PurchaseOrderResponseProto purchaseOrderResponse = 7; 2469 | optional ContentSyncResponseProto contentSyncResponse = 8; 2470 | optional GetAssetResponseProto getAssetResponse = 9; 2471 | optional GetImageResponseProto getImageResponse = 10; 2472 | optional RefundResponseProto refundResponse = 11; 2473 | optional PurchaseMetadataResponseProto purchaseMetadataResponse = 12; 2474 | optional GetSubCategoriesResponseProto subCategoriesResponse = 13; 2475 | optional UninstallReasonResponseProto uninstallReasonResponse = 15; 2476 | optional RateCommentResponseProto rateCommentResponse = 16; 2477 | optional CheckLicenseResponseProto checkLicenseResponse = 17; 2478 | optional GetMarketMetadataResponseProto getMarketMetadataResponse = 18; 2479 | repeated PrefetchedBundleProto prefetchedBundle = 19; 2480 | optional GetCategoriesResponseProto getCategoriesResponse = 20; 2481 | optional GetCarrierInfoResponseProto getCarrierInfoResponse = 21; 2482 | optional RestoreApplicationsResponseProto restoreApplicationResponse = 23; 2483 | optional QuerySuggestionResponseProto querySuggestionResponse = 24; 2484 | optional BillingEventResponseProto billingEventResponse = 25; 2485 | optional PaypalPreapprovalResponseProto paypalPreapprovalResponse = 26; 2486 | optional PaypalPreapprovalDetailsResponseProto paypalPreapprovalDetailsResponse = 27; 2487 | optional PaypalCreateAccountResponseProto paypalCreateAccountResponse = 28; 2488 | optional PaypalPreapprovalCredentialsResponseProto paypalPreapprovalCredentialsResponse = 29; 2489 | optional InAppRestoreTransactionsResponseProto inAppRestoreTransactionsResponse = 30; 2490 | optional InAppPurchaseInformationResponseProto inAppPurchaseInformationResponse = 31; 2491 | optional CheckForNotificationsResponseProto checkForNotificationsResponse = 32; 2492 | optional AckNotificationsResponseProto ackNotificationsResponse = 33; 2493 | optional PurchaseProductResponseProto purchaseProductResponse = 34; 2494 | optional ReconstructDatabaseResponseProto reconstructDatabaseResponse = 35; 2495 | optional PaypalMassageAddressResponseProto paypalMassageAddressResponse = 36; 2496 | optional GetAddressSnippetResponseProto getAddressSnippetResponse = 37; 2497 | } 2498 | optional PendingNotificationsProto pendingNotifications = 38; 2499 | } 2500 | message RestoreApplicationsRequestProto { 2501 | optional string backupAndroidId = 1; 2502 | optional string tosVersion = 2; 2503 | optional DeviceConfigurationProto deviceConfiguration = 3; 2504 | } 2505 | message RestoreApplicationsResponseProto { 2506 | repeated GetAssetResponseProto asset = 1; 2507 | } 2508 | message RiskHeaderInfoProto { 2509 | optional string hashedDeviceInfo = 1; 2510 | } 2511 | message SignatureHashProto { 2512 | optional string packageName = 1; 2513 | optional int32 versionCode = 2; 2514 | optional bytes hash = 3; 2515 | } 2516 | message SignedDataProto { 2517 | optional string signedData = 1; 2518 | optional string signature = 2; 2519 | } 2520 | message SingleRequestProto { 2521 | optional RequestSpecificPropertiesProto requestSpecificProperties = 3; 2522 | optional AssetsRequestProto assetRequest = 4; 2523 | optional CommentsRequestProto commentsRequest = 5; 2524 | optional ModifyCommentRequestProto modifyCommentRequest = 6; 2525 | optional PurchasePostRequestProto purchasePostRequest = 7; 2526 | optional PurchaseOrderRequestProto purchaseOrderRequest = 8; 2527 | optional ContentSyncRequestProto contentSyncRequest = 9; 2528 | optional GetAssetRequestProto getAssetRequest = 10; 2529 | optional GetImageRequestProto getImageRequest = 11; 2530 | optional RefundRequestProto refundRequest = 12; 2531 | optional PurchaseMetadataRequestProto purchaseMetadataRequest = 13; 2532 | optional GetSubCategoriesRequestProto subCategoriesRequest = 14; 2533 | optional UninstallReasonRequestProto uninstallReasonRequest = 16; 2534 | optional RateCommentRequestProto rateCommentRequest = 17; 2535 | optional CheckLicenseRequestProto checkLicenseRequest = 18; 2536 | optional GetMarketMetadataRequestProto getMarketMetadataRequest = 19; 2537 | optional GetCategoriesRequestProto getCategoriesRequest = 21; 2538 | optional GetCarrierInfoRequestProto getCarrierInfoRequest = 22; 2539 | optional RemoveAssetRequestProto removeAssetRequest = 23; 2540 | optional RestoreApplicationsRequestProto restoreApplicationsRequest = 24; 2541 | optional QuerySuggestionRequestProto querySuggestionRequest = 25; 2542 | optional BillingEventRequestProto billingEventRequest = 26; 2543 | optional PaypalPreapprovalRequestProto paypalPreapprovalRequest = 27; 2544 | optional PaypalPreapprovalDetailsRequestProto paypalPreapprovalDetailsRequest = 28; 2545 | optional PaypalCreateAccountRequestProto paypalCreateAccountRequest = 29; 2546 | optional PaypalPreapprovalCredentialsRequestProto paypalPreapprovalCredentialsRequest = 30; 2547 | optional InAppRestoreTransactionsRequestProto inAppRestoreTransactionsRequest = 31; 2548 | optional InAppPurchaseInformationRequestProto getInAppPurchaseInformationRequest = 32; 2549 | optional CheckForNotificationsRequestProto checkForNotificationsRequest = 33; 2550 | optional AckNotificationsRequestProto ackNotificationsRequest = 34; 2551 | optional PurchaseProductRequestProto purchaseProductRequest = 35; 2552 | optional ReconstructDatabaseRequestProto reconstructDatabaseRequest = 36; 2553 | optional PaypalMassageAddressRequestProto paypalMassageAddressRequest = 37; 2554 | optional GetAddressSnippetRequestProto getAddressSnippetRequest = 38; 2555 | } 2556 | message SingleResponseProto { 2557 | optional ResponsePropertiesProto responseProperties = 2; 2558 | optional AssetsResponseProto assetsResponse = 3; 2559 | optional CommentsResponseProto commentsResponse = 4; 2560 | optional ModifyCommentResponseProto modifyCommentResponse = 5; 2561 | optional PurchasePostResponseProto purchasePostResponse = 6; 2562 | optional PurchaseOrderResponseProto purchaseOrderResponse = 7; 2563 | optional ContentSyncResponseProto contentSyncResponse = 8; 2564 | optional GetAssetResponseProto getAssetResponse = 9; 2565 | optional GetImageResponseProto getImageResponse = 10; 2566 | optional RefundResponseProto refundResponse = 11; 2567 | optional PurchaseMetadataResponseProto purchaseMetadataResponse = 12; 2568 | optional GetSubCategoriesResponseProto subCategoriesResponse = 13; 2569 | optional UninstallReasonResponseProto uninstallReasonResponse = 15; 2570 | optional RateCommentResponseProto rateCommentResponse = 16; 2571 | optional CheckLicenseResponseProto checkLicenseResponse = 17; 2572 | optional GetMarketMetadataResponseProto getMarketMetadataResponse = 18; 2573 | optional GetCategoriesResponseProto getCategoriesResponse = 20; 2574 | optional GetCarrierInfoResponseProto getCarrierInfoResponse = 21; 2575 | optional RestoreApplicationsResponseProto restoreApplicationResponse = 23; 2576 | optional QuerySuggestionResponseProto querySuggestionResponse = 24; 2577 | optional BillingEventResponseProto billingEventResponse = 25; 2578 | optional PaypalPreapprovalResponseProto paypalPreapprovalResponse = 26; 2579 | optional PaypalPreapprovalDetailsResponseProto paypalPreapprovalDetailsResponse = 27; 2580 | optional PaypalCreateAccountResponseProto paypalCreateAccountResponse = 28; 2581 | optional PaypalPreapprovalCredentialsResponseProto paypalPreapprovalCredentialsResponse = 29; 2582 | optional InAppRestoreTransactionsResponseProto inAppRestoreTransactionsResponse = 30; 2583 | optional InAppPurchaseInformationResponseProto getInAppPurchaseInformationResponse = 31; 2584 | optional CheckForNotificationsResponseProto checkForNotificationsResponse = 32; 2585 | optional AckNotificationsResponseProto ackNotificationsResponse = 33; 2586 | optional PurchaseProductResponseProto purchaseProductResponse = 34; 2587 | optional ReconstructDatabaseResponseProto reconstructDatabaseResponse = 35; 2588 | optional PaypalMassageAddressResponseProto paypalMassageAddressResponse = 36; 2589 | optional GetAddressSnippetResponseProto getAddressSnippetResponse = 37; 2590 | } 2591 | message StatusBarNotificationProto { 2592 | optional string tickerText = 1; 2593 | optional string contentTitle = 2; 2594 | optional string contentText = 3; 2595 | } 2596 | message UninstallReasonRequestProto { 2597 | optional string assetId = 1; 2598 | optional int32 reason = 2; 2599 | } 2600 | message UninstallReasonResponseProto { 2601 | } 2602 | message CriticReviewsResponse { 2603 | optional string title = 1; 2604 | optional Image aggregateSentiment = 2; 2605 | optional uint32 totalNumReviews = 3; 2606 | optional uint32 percentFavorable = 4; 2607 | optional string sourceText = 5; 2608 | optional Link source = 6; 2609 | repeated Review review = 7; 2610 | } --------------------------------------------------------------------------------