
    u%jEo                        d dl mZmZ d dlmZ d dlmZ d dlmZ d dlm	Z	 d dl
mZ d dlmZ d dlZd	d
lmZmZmZ dZdZedz   Zedz   Zedz   Zedz   Zedz   Zedz   Zedz   Zedz   Zedz   Zedz   Zedz   Zedz   Z edz   Z!edz   Z"edz   Z#edz   Z$edz   Z%dZ&dZ'd Z( G d! d"e)      Z* G d# d$e)      Z+ G d% d&e)      Z, G d' d(e-      Z.y))    )	b64decodeurlsafe_b64encode)datetime)encode_dss_signature)default_backend)hashes)load_der_public_key)paddingN   )googleplay_pb2configutilsTz#https://android.clients.google.com/zfdfe/checkinauthuploadDeviceConfigsearchdetailshomeV2browsedeliverypurchasesearchSuggestbulkDetailslogtoc	acceptToslistrevz1oauth2:https://www.googleapis.com/auth/googleplayz0application/x-www-form-urlencoded; charset=UTF-8zapplication/x-protobufc                       e Zd Zd Zd Zy)
LoginErrorc                     || _         y Nvalueselfr$   s     ^/home/ubuntu/.local/share/pipx/venvs/gplaycli/lib/python3.12/site-packages/gpapi/googleplay.py__init__zLoginError.__init__,   	    
    c                 ,    t        | j                        S r"   reprr$   r&   s    r'   __str__zLoginError.__str__/       DJJr*   N__name__
__module____qualname__r(   r/    r*   r'   r    r    +        r*   r    c                       e Zd Zd Zd Zy)RequestErrorc                     || _         y r"   r#   r%   s     r'   r(   zRequestError.__init__4   r)   r*   c                 ,    t        | j                        S r"   r,   r.   s    r'   r/   zRequestError.__str__7   r0   r*   Nr1   r5   r*   r'   r8   r8   3   r6   r*   r8   c                       e Zd Zd Zd Zy)SecurityCheckErrorc                     || _         y r"   r#   r%   s     r'   r(   zSecurityCheckError.__init__;   r)   r*   c                 ,    t        | j                        S r"   r,   r.   s    r'   r/   zSecurityCheckError.__str__>   r0   r*   Nr1   r5   r*   r'   r<   r<   :   r6   r*   r<   c                       e Zd ZdZ	 	 ddZd Zd Zd Zd Zd dZ	d	 Z
d
 Zd!dZd Zd ZdedfdZd Zd Zd Zd Zd"dZd#dZd$dZ	 	 d%dZd Z	 	 d&dZd'dZd Zd Zd Zed        Z ed        Z!y)(GooglePlayAPIzGoogle Play Unofficial API Class

    Usual APIs methods are login(), search(), details(), bulkDetails(),
    download(), browse(), reviews() and list().Nc                     d | _         d | _        d | _        d | _        d | _        || _        t        j                  |      | _        | j                  |       | j                  |       y r"   )authSubTokengsfIddevice_config_tokendeviceCheckinConsistencyToken	dfeCookieproxies_configr   DeviceBuilderdeviceBuilder	setLocalesetTimezone)r&   localetimezonedevice_codenamerG   s        r'   r(   zGooglePlayAPI.__init__H   s]     
#' -1*,#11/Bv"r*   c                 :    | j                   j                  |       y r"   )rI   rJ   )r&   rL   s     r'   rJ   zGooglePlayAPI.setLocaleT   s    $$V,r*   c                 :    | j                   j                  |       y r"   )rI   rK   )r&   rM   s     r'   rK   zGooglePlayAPI.setTimezoneW   s    &&x0r*   c           	         t        t        j                        }t        j                  |d      }t        j
                  |dd d|       }t        j                  ||dz         }t        j
                  ||dz   d d|       }t        j                  t        j                         t                     }|j                  |       d|j                         dd z   }	t        ||      }
t        |
t                     }|j                         dz   |j                         z   }|j                  |t!        j"                  t!        j$                  t        j                               t        j                         d            }t'        |	|z         S )	zNEncrypt credentials using the google publickey, with the
        RSA algorithmr      N   )backend    )	algorithm)mgfrV   label)r   r   GOOGLE_PUBKEYr   readInttoBigIntr   HashSHA1r   updatefinalizer   r	   encodeencryptr
   OAEPMGF1r   )r&   loginpasswd	binaryKeyimodulusjexponentdigesthder_data	publicKeyto_be_encrypted
ciphertexts                 r'   encryptPasswordzGooglePlayAPI.encryptPasswordZ   s:    f223	MM)Q'..12q!34MM)QU+>>)AEF"3Aa"89 V[[]O4EFi foo'!,, (:'/:KL	  ,,.72V]]_D&&LLLL6;;=9 ++-

 !Z00r*   c                     || _         y r"   )rB   )r&   rB   s     r'   setAuthSubTokenzGooglePlayAPI.setAuthSubToken   s
    (r*   c                    |r| j                   j                         }n| j                   j                         }| j                  dj	                  | j                        |d<   | j
                  d| j
                  z  |d<   | j                  | j                  |d<   | j                  | j                  |d<   | j                  | j                  |d<   |S )ziReturn the default set of request headers, which
        can later be expanded, based on the request type{0:x}zX-DFE-Device-Idz	Bearer %sAuthorizationzX-DFE-Device-Config-Tokenz&X-DFE-Device-Checkin-Consistency-TokenzX-DFE-Cookie)	rI   getDeviceUploadHeadersgetBaseHeadersrC   formatrB   rD   rE   rF   )r&   upload_fieldsheaderss      r'   
getHeaderszGooglePlayAPI.getHeaders   s     ((??AG((779G::!)0

)CG%&('2T5F5F'FGO$##/373K3KG/0--9@D@b@bG<=>>%&*nnGN#r*   c                    | j                         }t        |d<   | j                  j                         }|j	                         }t        j                  t        ||t        | j                        }t        j                         }|j                  |j                         |j                  | _        |j                  |_        |j"                  |_        |j$                  j'                  d|z   dz          |j$                  j'                  |       |j	                         }t        j                  t        ||t        | j                         |j                  S )NContent-Typedatar{   verifyproxies[])r|   CONTENT_TYPE_PROTOrI   getAndroidCheckinRequestSerializeToStringrequestspostCHECKIN_URL
ssl_verifyrG   r   AndroidCheckinResponseParseFromStringcontentrE   	androidIdidsecurityTokenaccountCookieappend)r&   email
ac2dmTokenr{   requeststringRequestresresponses           r'   r   zGooglePlayAPI.checkin   s   //#"4$$==?113mmKm$+J$($7$79 "88:  --5-S-S* ''
 ( 6 6$$S5[3%67$$Z0113k(%'"11		3 !!!r*   c                 F   t        j                         }|j                  j                  | j                  j                                | j                  d      }|j                         }t        j                  t        ||t        d| j                        }t         j                  j                  |j                        }	 |j                   j#                  d      r7|j                   j$                  | _        | j&                  j(                  | _        yy# t*        $ r Y yw xY w)zrUpload the device configuration of the fake device
        selected in the __init__ methodi to the google account.T)rz   <   r   r{   r   timeoutr   uploadDeviceConfigResponseN)r   UploadDeviceConfigRequestdeviceConfigurationCopyFromrI   getDeviceConfigr|   r   r   r   
UPLOAD_URLr   rG   ResponseWrapper
FromStringr   payloadHasFieldr   rD   uploadDeviceConfigToken
ValueError)r&   uploadr{   r   r   s        r'   r   z GooglePlayAPI.uploadDeviceConfig   s      99;""++D,>,>,N,N,PQ///5002==-)0(2)+)-)<)<	>
 "11<<X=M=MN	(()EF+3+;+;+V+V(+/+C+C+[+[( G  		s   AD 	D D c                    ||| j                  ||      j                  d      }| j                  j                  ||      }d|d<   d|d<   d|d<   | j                  j	                  | j
                        }d	|d
<   t        j                  t        |t        | j                        }|j                  j                         }	i }|	D ]K  }
d|
vr|
j                  dd      \  }}|j                         ||j                         j                         <   M d|v r|d   }n2d|v r#d|d   v rt        d      t!        d|d   z         t!        d      | j#                  ||      | _        | j%                  ||       | j'                          y|,|*|| _        | j)                  |       | j+                  d       yt!        d      )zLogin to your Google Account.
        For first time login you should provide:
            * email
            * password
        For the following logins you need to provide:
            * gsfId
            * authSubTokenNutf-8ac2dmservice1add_accountzcom.google.android.gms	callerPkgzcom.google.android.gsmapp)r   r   r   =r   r   errorNeedsBrowserzSecurity check is needed, try to visit https://accounts.google.com/b/0/DisplayUnlockCaptcha to unlock, or setup an app-specific passwordserver says: Auth token not found.drvz6Either (email,pass) or (gsfId, authSubToken) is needed)rq   decoderI   getLoginParamsgetAuthHeadersrC   r   r   AUTH_URLr   rG   textsplitstriplowerr<   r    r   getAuthSubTokenr   rs   r   )r&   r   passwordrC   rB   encryptedPassparamsr{   r   r   dkvr   s                 r'   rd   zGooglePlayAPI.login   s    !5 !00AHHQM''66umLF 'F9$'F=!":F;((77

CG5GEN}}XF:-1-@-@BH==&&(DF 6a<wwsA1,-GGIqwwy()	6
 #F^
F"!VG_4, .T U U !6'?!BCC !899eZ8DJ  6##%<#;DJ  .KKUVVr*   c                 l   | j                   j                  ||      }d|d<   d|d<   | j                   j                  | j                        }d|d<   t	        j
                  t        |t        || j                        }|j                  j                         }i }|D ]K  }d|vr|j                  dd      \  }	}
|
j                         ||	j                         j                         <   M d|v r)|d   }| j                  ||      }| j                  |       y d	|v rt        d
|d	   z         t        d      )Nandroidmarketr   com.android.vendingr   )r   r   r{   r   r   r   tokenr   r   zauth token not found.)rI   r   r   rC   r   r   r   r   rG   r   r   r   r   getSecondRoundTokenrs   r    )r&   r   re   requestParamsr{   r   r   r   r   r   r   master_tokensecond_round_tokens                r'   r   zGooglePlayAPI.getAuthSubToken   s2   **99%H#2i 4e$$33DJJ?.==&3(2)0)-)<)<	>
 }}""$ 	2A!|773?DAq()	F1779??$%		2
 f!'?L!%!9!9,!V  !34_vg>??455r*   c                    | j                   dj                  | j                         |d<   ||d<   d|d<   d|d<   d|d<   d|d	<   t        |d
<   |j                  d       |j                  d       | j                  j                  | j                         }d|d<   t        j                  t        ||t        | j                        }|j                  j                         }i }|D ]K  }d|vr|j                  dd      \  }}|j                         ||j                         j                         <   M d|v r|d   S d|v rt        d|d   z         t        d      )Nru   r   Tokenr   check_emailzCAA4AQ==token_request_optionssystem_partition#_opt_is_called_from_account_managerr   EmailEncryptedPasswdr   r   r   r   r   r   r   r   r   )rC   ry   OAUTH_SERVICEpoprI   r   r   r   r   r   rG   r   r   r   r   r    )	r&   first_tokenr   r{   r   r   r   r   r   s	            r'   r   z!GooglePlayAPI.getSecondRoundToken  s]   ::!").."<F;%w #}*4&'%(!"8;45)y

7

$%$$33DJJ?.==&,)0(2)-)<)<	>
 }}""$ 	2A!|773?DAq()	F1779??$%		2
 V&>!_vg>??455r*   c           	         | j                   t        d      | j                         }||d<   |4t        j                  |t        |      ||t        d| j                        }n)t        j                  |||t        d| j                        }t        j                  j                  |j                        }|j                  j                  dk7  rt        |j                  j                        |S )N.You need to login before executing any requestr~   r   )r   r{   r   r   r   r   r{   r   r   r   r    )rB   r    r|   r   r   strr   rG   getr   r   r   r   commandsdisplayErrorMessager8   )r&   path	post_datacontent_typer   r{   r   messages           r'   executeRequestApi2z GooglePlayAPI.executeRequestApi2:  s    $MNN//#". }}T*-i.-4,2,6-/-1-@-@BH  ||D,3+1+5,.,0,?,?AH !00;;H<L<LM//25w//CCDDr*   c                     dt         j                  j                  |      ddd}| j                  t        |      }|j
                  j                  j                  }t        t        t        j                  |            S )N31202)cqssissst)r   )r   r   quoter   SEARCH_SUGGEST_URLr   searchSuggestResponseentryr   mapparseProtobufObj)r&   queryr   r   entryIterators        r'   r   zGooglePlayAPI.searchSuggestV  si    ~~++E2 &&'9&&I::@@C..>??r*   c                    | j                   t        d      t        dj                  t        j
                  j                  |            z   }| j                          | j                  |      }t        j                  |      r|j                  d   j                  }n|}|j                  j                  j                  }t        t!        t
        j"                  |            S )z Search the play store for an app.

        nb_result (int): is the maximum number of result to be returned

        offset (int): is used to take result starting from an index.
        r   z	?c=3&q={}r   )rB   r    
SEARCH_URLry   r   r   r   r   r   hasPrefetchpreFetchr   r   listResponsedocr   r   r   )r&   r   r   r   r   resIterators         r'   r   zGooglePlayAPI.search_  s     $MNNK..x~~/C/CE/JKK
&&t,T"}}Q'00HH&&3377C..<==r*   c                     t         dj                  t        j                  j	                  |            z   }| j                  |      }t        j                  |j                  j                  j                        S )znGet app details from a package name.

        packageName is the app unique ID (usually starting with 'com.').z?doc={})
DETAILS_URLry   r   r   r   r   r   r   detailsResponsedocV2)r&   packageNamer   r   s       r'   r   zGooglePlayAPI.detailst  sY     Y--hnn.B.B;.OPP&&t,%%dll&B&B&H&HIIr*   c                    ddi}t        j                         }|j                  j                  |       |j	                         }| j                  t        |j                  d      t        |      }|j                  j                  }|j                  D cg c]8  }t        j                  |      sdnt        j                  |j                        : c}S c c}w )a  Get several apps details from a list of package names.

        This is much more efficient than calling N times details() since it
        requires only one request. If an item is not found it returns an empty object
        instead of throwing a RequestError('Item not found') like the details() function

        Args:
            packageNames (list): a list of app IDs (usually starting with 'com.').

        Returns:
            a list of dictionaries containing docv2 data, or None
            if the app doesn't existaur   r   )r   r   r   N)r   BulkDetailsRequestdocidextendr   r   BULK_URLr   r   r   bulkDetailsResponser   r   hasDocr   r   )r&   packageNamesr   reqr   r   r   r   s           r'   r   zGooglePlayAPI.bulkDetails|  s     //1		&$$&))(48KK4H7I17 * 9 ??66 &^^- "LL/&&uyy12 - 	- -s   =Cc                 J   t         dz   }||dj                  |      z  }| j                  |      }t        j                  |      r|j
                  d   j                  }n|}|j                  j                  j                  }t        t        t        j                  |            S )Nz?c=3&nocache_isui=true&cat={}r   )HOME_URLry   r   r   r   r   r   r   r   r   r   r   r   )r&   catr   r   r   r   s         r'   homezGooglePlayAPI.home  s    22?I$$S))D&&t,T"}}Q'00HH&&3377C..<==r*   c                 T   t         dz   }|1|dj                  t        j                  j	                  |            z  }|1|dj                  t        j                  j	                  |            z  }| j                  |      }t        j                  |j                  j                        S )zBrowse categories. If neither cat nor subcat are specified,
        return a list of categories, otherwise it return a list of apps
        using cat (category ID) and subCat (subcategory ID) as filters.z?c=3r  &ctr={})	
BROWSE_URLry   r   r   r   r   r   r   browseResponse)r&   r  subCatr   r   s        r'   r   zGooglePlayAPI.browse  s     F"?I$$X^^%9%9#%>??DI$$X^^%9%9&%ABBD&&t,%%dll&A&ABBr*   c                    t         dj                  t        j                  j	                  |            z   }|1|dj                  t        j                  j	                  |            z  }|:|dj                  t        j                  j	                  t        |                  z  }|:|dj                  t        j                  j	                  t        |                  z  }| j                  |      }g }g }|u|j                  D ]L  }	|	j                  j                  j                  j                  D ]  }
|j                  |
j                          N |D cg c]  }|j                   c}S g }|j                  j                  j                  D ]H  }|j                  D ]7  }|j                  D ]&  }|j                  t        j                   |             ( 9 J |S c c}w )a<  List all possible subcategories for a specific category. If
        also a subcategory is provided, list apps from this category.

        Args:
            cat (str): category id
            ctr (str): subcategory id
            nb_results (int): if a subcategory is specified, limit number
                of results to this number
            offset (int): if a subcategory is specified, start counting from this
                result
        Returns:
            A list of categories. If subcategory is specified, a list of apps in this
            category.
        z?c=3&cat={}r  &n={}&o={})LIST_URLry   r   r   r   r   r   r   r   r   r   r   r  childr  r   r   )r&   r  ctr
nb_resultsoffsetr   r   clustersdocspfclusterr   appsr   as                  r'   r   zGooglePlayAPI.list  s    -..x~~/C/CC/HII?I$$X^^%9%9#%>??D!GNN8>>#7#7J#HIIDGNN8>>#7#7F#DEED&&t,;mm 3!{{22??CC 3GOOGMM233 &..AGG..D\\..22 ? ?AWW ?E$:$:1$=>??? K /s   Gc                    t         dj                  t        j                  j	                  |      |      z   }||dj                  |      z  }||dj                  |      z  }|r|dz  }| j                  |      }g }|j                  j                  j                  j                  D ]&  }	|j                  t        j                  |	             ( |S )a  Browse reviews for an application

        Args:
            packageName (str): app unique ID.
            filterByDevice (bool): filter results for current device
            sort (int): sorting criteria (values are unknown)
            nb_results (int): max number of reviews to return
            offset (int): return reviews starting from an offset value

        Returns:
            dict object containing all the protobuf data returned from
            the api
        z?doc={}&sort={}r  r  z&dfil=1)REVIEWS_URLry   r   r   r   r   r   reviewResponsegetResponsereviewr   r   )
r&   r  filterByDevicesortr  r  r   r   outputr*  s
             r'   reviewszGooglePlayAPI.reviews  s      .55hnn6J6J;6WY]^^!GNN:..DGNN6**DID&&t,ll11==DD 	:FMM%0089	:r*   c           	          | j                         }t        j                  |||t        dd| j                        }|j
                  j                  d      }d}|j                  |      ||dS )NTr   )r{   cookiesr   streamr   r   zcontent-lengthi   )
chunk_size)r   
total_sizer2  )r|   r   r   r   rG   r{   iter_content)r&   urlr0  r{   r   r3  r2  s          r'   _deliver_datazGooglePlayAPI._deliver_data  sr    //#<<W(/
'+R(,(;(;= %%))*:;
#
 ---D((* 	*r*   c                    |@| j                  |      j                  d      j                  d      }|j                  d      }t        |      |t        |      d}| j                         }|||d<   t	        j                  t
        ||t        d| j                        }	t        j                  j                  |	j                        }	|	j                  j                  d	k7  rt        |	j                  j                        |	j                  j                   j"                  j$                  d	k(  rt        d
      i }
||
d<   g |
d<   g |
d<   |	j                  j                   j"                  j$                  }|	j                  j                   j"                  j&                  d   }t        |j(                        t        |j*                        i}| j-                  ||      |
d<   |	j                  j                   j"                  j.                  D ]F  }i }|j(                  |d<   | j-                  |j$                  d      |d<   |
d   j1                  |       H |s|
S |	j                  j                   j"                  j2                  D ]_  }i }|j4                  dk(  rd}nd}||d<   |j6                  |d<   | j-                  |j$                  d      |d<   |
d   j1                  |       a |
S )a  Download an already purchased app.

        Args:
            packageName (str): app unique ID (usually starting with 'com.')
            versionCode (int): version to download
            offerType (int): different type of downloads (mostly unused for apks)
            downloadToken (str): download token returned by 'purchase' API
            progress_bar (bool): wether or not to print a progress bar to stdout

        Returns:
            Dictionary containing apk data and a list of expansion files. As stated
            in android documentation, there can be at most 2 expansion files, one with
            main content, and one for patching the main content. Their names should
            follow this format:

            [main|patch].<expansion-version>.<package-name>.obb

            Data to build this name string is provided in the dict object. For more
            info check https://developer.android.com/google/play/expansion-files.html
        Nr   
appDetailsversionCodeotr   vcdtokr   r   r   zApp not purchaseddocIdadditionalDatasplitsr   filenamemainpatchtype)r   r   r   r|   r   DELIVERY_URLr   rG   r   r   r   r   r   r   r8   r   deliveryResponseappDeliveryDatadownloadUrldownloadAuthCookierB  r$   r6  r   r   additionalFilefileTyper9  )r&   r  r9  	offerTypedownloadTokenexpansion_filesr8  r   r{   r   resultrI  cookier0  r   r%  obbobbTypes                     r'   r   zGooglePlayAPI.delivery   s   . k266yAEElSJ$..7KI$K(* //#$*F6N<<g'-j(*(,(;(;= "11<<X=M=MN00B6x00DDEE..>>JJbP233F)F7O')F#$!F8"**;;KKWWK%%66FFYYZ[\FFKK #fll"3G "//WEF6N!))::JJPP +!JJ&	 ..u/@/@$G&	x ''*	+ #''88HHWW 3 <<1$$G%G#&	#&??-  ..sE&	'(//23 Mr*   c                    | j                   t        d      |@| j                  |      j                  d      j                  d      }|j                  d      }| j	                         }t        |      |t        |      d}| j                  |       t        j                  t        ||t        d| j                        }t        j                  j                  |j                        }|j                   j"                  dk7  rt%        |j                   j"                        |j&                  j(                  j*                  }	| j-                  ||||	|	      S )
a  Download an app and return its raw data (APK file). Free apps need
        to be "purchased" first, in order to retrieve the download cookie.
        If you want to download an already purchased app, use *delivery* method.

        Args:
            packageName (str): app unique ID (usually starting with 'com.')
            versionCode (int): version to download
            offerType (int): different type of downloads (mostly unused for apks)
            downloadToken (str): download token returned by 'purchase' API
            progress_bar (bool): wether or not to print a progress bar to stdout

        Returns
            Dictionary containing apk data and optional expansion files
            (see *delivery*)
        r   r   r8  r9  r:  r   r   r   )rO  )rB   r    r   r   r|   r   r   r   r   PURCHASE_URLr   rG   r   r   r   r   r   r   r8   r   buyResponserN  r   )
r&   r  r9  rM  rO  r8  r{   r   r   dlTokens
             r'   downloadzGooglePlayAPI.downloadM  s-   " $MNNk266yAEElSJ$..7K//#I$K(* 	==w(.z)+)-)<)<>
 "11<<X=M=MN00B6x00DDEE&&22@@G==k9g1@ ! B Br*   c                    t        j                         }d|z   |_        t        t	        j
                         j                               }||_        |j                         }t        j                  t        || j                         t        d| j                        }t         j                  j                  |j                         }|j"                  j$                  dk7  rt'        |j"                  j$                        y )NzconfirmFreeDownload?doc=r   r   r   )r   
LogRequestdownloadConfirmationQueryintr   now	timestampr   r   r   LOG_URLr|   r   rG   r   r   r   r   r   r8   )r&   r  log_requestr^  string_requestr   s         r'   r   zGooglePlayAPI.logx  s    $//10JU0R-0023	 )$668==&4)-):(2)+)-)<)<> "11<<X=M=MN00B6x00DDEE 7r*   c                    t        j                  t        | j                         t        d| j
                        }t        j                  j                  |j                        }|j                  j                  }t        j                  |      r0t        j                  |      r| j                  |j                          t        j"                  |      r|j$                  | _        t        j(                  |      S )Nr   )r{   r   r   r   )r   r   TOC_URLr|   r   rG   r   r   r   r   r   tocResponser   hasTosContenthasTosTokenr   tosToken	hasCookierQ  rF   r   )r&   r   r   rd  s       r'   r   zGooglePlayAPI.toc  s    <<'+'8&0')'+':':	<
 --889I9IJll..{+0A0A+0NNN;//0??;'(//DN%%k22r*   c                 &   |dd}t        j                  t        | j                         |t        d| j
                        }t        j                  j                  |j                        }t        j                  |j                  j                        S )Nfalse)tosttoscmer   r   )r   r   ACCEPT_TOS_URLr|   r   rG   r   r   r   r   r   r   r   acceptTosResponse)r&   rg  r   r   r   s        r'   r   zGooglePlayAPI.acceptTos  sv    
 <<'+'8&,&0')'+':':< --889I9IJ%%dll&D&DEEr*   c                  *    t        j                         S r"   )r   getDevicesCodenamesr5   r*   r'   rp  z!GooglePlayAPI.getDevicesCodenames  s    ))++r*   c                  *    t        j                         S r"   )r   getDevicesReadableNamesr5   r*   r'   rr  z%GooglePlayAPI.getDevicesReadableNames  s    --//r*   )en_USUTCbaconN)F)NNNNr"   )NN)NNN)F   NN)Nr   NF)Nr   F)"r2   r3   r4   __doc__r(   rJ   rK   rq   rs   r|   r   r   rd   r   r   CONTENT_TYPE_URLENCr   r   r   r   r   r  r   r   r.  r6  r   rX  r   r   r   staticmethodrp  rr  r5   r*   r'   r@   r@   B   s    3
 HO $
#-1'1R)("8*2Wh666@ 26DW`d 8@>*J-6
>C%N ?@(,:
* AB5:KZ)BVF"3F , , 0 0r*   r@   )/base64r   r   r   /cryptography.hazmat.primitives.asymmetric.utilsr   cryptography.hazmat.backendsr   cryptography.hazmat.primitivesr   ,cryptography.hazmat.primitives.serializationr	   )cryptography.hazmat.primitives.asymmetricr
   r   r   r   r   r   r   BASEFDFEr   r   r   r   r   r  r  rF  rU  r   r	  r_  rc  rm  r  r'  r   rx  r   	Exceptionr    r8   r<   objectr@   r5   r*   r'   <module>r     s   0  P 8 1 L =  + +
,g~Y&=((
H_
Y(?H_
j j O+ -
,
,#&=UlCH -     9     j	0F j	0r*   