Added bodzone for android
This commit is contained in:
parent
720d435c05
commit
bf84bce6b7
1936 changed files with 1006688 additions and 0 deletions
368
BotZone2.8v1 Android/Lrfarm.py
Normal file
368
BotZone2.8v1 Android/Lrfarm.py
Normal file
|
|
@ -0,0 +1,368 @@
|
|||
import commands
|
||||
from colorama import init, Fore, Back, Style
|
||||
# Coloroma autoreset
|
||||
init(autoreset=True)
|
||||
|
||||
|
||||
def t():
|
||||
|
||||
print(" Lr Trunks")
|
||||
stage = input('What stage would you like to complete(Proud Bloodline 320022) : ')
|
||||
difficulty = input('Enter the difficulty|(2:Z-Hard): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete(Raging Counterstrike 406003) : ')
|
||||
difficulty = input('Enter the difficulty|(2:Z-Hard): ')
|
||||
loop = input('Enter how many times to execute(5): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete(Dignity of a Clan 408002) : ')
|
||||
difficulty = input('Enter the difficulty|(2:Z-Hard): ')
|
||||
loop = input('Enter how many times to execute(5): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete(The Strongest Space Pirate 420002) : ')
|
||||
difficulty = input('Enter the difficulty|(2:Z-Hard): ')
|
||||
loop = input('Enter how many times to execute(5): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete(Warrior of Hope 414002) : ')
|
||||
difficulty = input('Enter the difficulty|(2:Z-Hard): ')
|
||||
loop = input('Enter how many times to execute(5): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete(The Time Patrol Warrior 422002) : ')
|
||||
difficulty = input('Enter the difficulty|(2:Z-Hard): ')
|
||||
loop = input('Enter how many times to execute(5): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def mv():
|
||||
print("Majin Vegeta")
|
||||
stage = input('What stage would you like to complete(The Dark Prince Returns 319022) : ')
|
||||
difficulty = input('Enter the difficulty|(2:Z-Hard): ')
|
||||
loop = input('Enter how many times to execute(15): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete(The Strongest Shadow Dragon 517002) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(3): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete(Dark Nightmare 518002) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(3): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete(Fusion in Blue 519001) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(3): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete(Rose-Tinted Plot 520001) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(3): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete(A New Hope 522001) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(3): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def gv():
|
||||
print(" Lr Super Saiyan Goku & Super Saiyan Vegeta")
|
||||
stage = input('What stage would you like to complete(The Ultimate Pair of the Present World 537001) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete(Ultimate Splendor 512003) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(7): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def b():
|
||||
print("Lr Full Power Boujack (Galactic Warrior)")
|
||||
stage = input('What stage would you like to complete( Extreme Peril 306008) : ')
|
||||
difficulty = input('Enter the difficulty|(2:Z-Hard): ')
|
||||
loop = input('Enter how many times to execute(25): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def c():
|
||||
print("Lr Cell (Perfect Form) & Cell Jr")
|
||||
stage = input('What stage would you like to complete(Waking Nightmare 502003) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(7): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def tm():
|
||||
print("Lr Trunks (Teen) (Future) & Mai (Future)")
|
||||
stage = input('What stage would you like to complete( The Zero Mortals Plan 328006) : ')
|
||||
difficulty = input('Enter the difficulty|(2:Z-Hard): ')
|
||||
loop = input('Enter how many times to execute(20): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
"BREAK"
|
||||
stage = input('What stage would you like to complete(Dark Nightmare 518002) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(1): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete( Rose-Tinted Plot 520001) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(1): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
"BREAK"
|
||||
stage = input('What stage would you like to complete(Fusion in Blue 519001) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(1): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete( The Epic Battle Begins 524001) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(1): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
"BREAK"
|
||||
stage = input('What stage would you like to complete(A New Hope 522001) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(2): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete( Last Judgment...Or Last Hope 523002) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(2): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
"BREAK"
|
||||
stage = input('What stage would you like to complete(Searing Rose-Colored Fury 520002) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(2): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete( Sublime Blue! 519002) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(2): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def bw():
|
||||
print("Lr Beerus & Whis")
|
||||
stage = input('What stage would you like to complete(God of Destruction Wrath 511002) : ')
|
||||
difficulty = input('Enter the difficulty|(2:Z-Hard): ')
|
||||
loop = input('Enter how many times to execute(7): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete(Destruction God Awakens 314001) : ')
|
||||
difficulty = input('Enter the difficulty|(2:Z-Hard): ')
|
||||
loop = input('Enter how many times to execute(4): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
"BREAK"
|
||||
stage = input('What stage would you like to complete(In Search of the Super Saiyan God 314002) : ')
|
||||
difficulty = input('Enter the difficulty|(2:Z-Hard): ')
|
||||
loop = input('Enter how many times to execute(7): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete(Vegeta Pride 314007) : ')
|
||||
difficulty = input('Enter the difficulty|(2:Z-Hard): ')
|
||||
loop = input('Enter how many times to execute(4): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def gg():
|
||||
print("Lr Super Saiyan Gohan (Teen) & Super Saiyan Goten (Kid)")
|
||||
stage = input('What stage would you like to complete(Go! Warriors of the New Generation 552001) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(10): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete(Blast! Family Kamehameha! 326006) : ')
|
||||
difficulty = input('Enter the difficulty|(2:Z-Hard): ')
|
||||
loop = input('Enter how many times to execute(20): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def vg():
|
||||
print("Lr Super Saiyan Goku (Angel) & Super Saiyan Vegeta (Angel)")
|
||||
stage = input('What stage would you like to complete(The Ultimate Pair of the Otherworld 536001) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(10): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete(Fusion Reborn! 326006) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(7): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def tg():
|
||||
print("Lr Trunks (Kid) & Goten (Kid)")
|
||||
stage = input('What stage would you like to complete(An Unexpectedly Powerful Man! 411002) : ')
|
||||
difficulty = input('Enter the difficulty|(2:Z-Hard): ')
|
||||
loop = input('Enter how many times to execute(5): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete(Super Gotenks! 513002) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(3): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
"BREAK"
|
||||
stage = input('What stage would you like to complete(One Powerful Super Fusion! 513003) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(3): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def gh():
|
||||
print(" Lr Super Saiyan 2 Gohan")
|
||||
stage = input('What stage would you like to complete(Waking Nightmare 502003) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def ggg():
|
||||
print(" Lr Super Saiyan 3 Goku")
|
||||
stage = input('What stage would you like to complete(Super Saiyan Goku 403002) : ')
|
||||
difficulty = input('Enter the difficulty|(2:Z-Hard): ')
|
||||
loop = input('Enter how many times to execute(5): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete(Phantom Majin Resurrected! 535002) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(3): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
"BREAK"
|
||||
stage = input('What stage would you like to complete(Mighty Warrior: 24-Hour Revival 528001) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(3): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete(Ultimate Finishing Move 504002) : ')
|
||||
difficulty = input('Enter the difficulty|(2:Z-Hard): ')
|
||||
loop = input('Enter how many times to execute(4): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def bd():
|
||||
print("Lr Bardock")
|
||||
stage = input('What stage would you like to complete(Saiyans from Planet Vegeta 347001) : ')
|
||||
difficulty = input('Enter the difficulty|(2:Z-Hard): ')
|
||||
loop = input('Enter how many times to execute(10): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete( 534001 The Unknown Battle) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(3): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
"BREAK"
|
||||
stage = input('What stage would you like to complete(347007 A Lone Warriors Last Battle) : ')
|
||||
difficulty = input('Enter the difficulty|(2:Z-Hard): ')
|
||||
loop = input('Enter how many times to execute(3): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete( 602002 True Fear) : ')
|
||||
difficulty = input('Enter the difficulty|(2:Z-Hard): ')
|
||||
loop = input('Enter how many times to execute(3): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
"BREAK"
|
||||
stage = input('What stage would you like to complete(602003 Summit of the Universe) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(5): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def gb():
|
||||
print("Lr Goku Black (Super Saiyan Rosé) & Zamasu")
|
||||
stage = input('What stage would you like to complete( 518002 Dark Nightmare) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
"BREAK"
|
||||
stage = input('What stage would you like to complete(519001 Fusion in Blue) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(3): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete( 520001 Rose-Tinted Plot) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(3): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
"BREAK"
|
||||
stage = input('What stage would you like to complete(522001 A New Hope) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(3): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete( 523002 Last Judgment...Or Last Hope) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(3): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
"BREAK"
|
||||
stage = input('What stage would you like to complete(524001 The Epic Battle Begins) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(3): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def gf():
|
||||
print("Lr Goku & Frieza (Final Form) (Angel)")
|
||||
stage = input('What stage would you like to complete( 544001 Ever-Evolving Power) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(5): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
"BREAK"
|
||||
stage = input('What stage would you like to complete(Ever-Evolving Evil) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(5): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
stage = input('What stage would you like to complete(538001 Kaboom! Ultra Instinct) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(5): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
"BREAK"
|
||||
stage = input('What stage would you like to complete(533002 The True Golden Frieza) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(5): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def by():
|
||||
print("Lr Legendary Super Saiyan Broly")
|
||||
stage = input('What stage would you like to complete(501003 The Demon Returns) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
38
BotZone2.8v1 Android/README.md
Normal file
38
BotZone2.8v1 Android/README.md
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# pysqlsimplecipher
|
||||
Encrypt or decrypt formated sqlite db.
|
||||
|
||||
This project is a tool for sqlite database encryption or decryption like
|
||||
[sqlcipher](http://sqlcipher.net/)
|
||||
without install sqlcipher.
|
||||
|
||||
When encrypt or decrypt database, an algorithm called AES-256-CBC is used.
|
||||
Each page shares the same key derived from password,
|
||||
but owns a random initialization vector stored at the end of the page.
|
||||
|
||||
## Decrypt
|
||||
```bash
|
||||
python decrypt.py encrypted.db password output.db
|
||||
```
|
||||
|
||||
## Encrypt
|
||||
```bash
|
||||
python encrypt.py plain.db password output.db
|
||||
```
|
||||
Needs reserved space at the end of each page of the database file.
|
||||
|
||||
Otherwise, use sqlcipher to encrypt.
|
||||
|
||||
#### Encrypt with sqlcipher
|
||||
- Open plain db
|
||||
```bash
|
||||
./sqlcipher plain.db
|
||||
```
|
||||
- Encrypt to enc.db
|
||||
```sql
|
||||
ATTACH DATABASE 'enc.db' as encrypted key 'testkey';
|
||||
SELECT sqlcipher_export('encrypted');
|
||||
DETACH DATABASE encrypted;
|
||||
```
|
||||
|
||||
## License
|
||||
GNU Lesser General Public License Version 3
|
||||
211
BotZone2.8v1 Android/TapOnMe.py
Normal file
211
BotZone2.8v1 Android/TapOnMe.py
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
from colorama import init, Fore, Back, Style
|
||||
import commands
|
||||
import config
|
||||
import sys
|
||||
import os
|
||||
import webbrowser
|
||||
|
||||
# Coloroma autoreset
|
||||
init(autoreset=True)
|
||||
if not os.path.isdir("Saves"):
|
||||
try:
|
||||
os.mkdir('Saves')
|
||||
os.mkdir('Saves/ios')
|
||||
os.mkdir('Saves/android')
|
||||
os.mkdir('Saves/Jp')
|
||||
os.mkdir('Saves/Jp/ios')
|
||||
os.mkdir('Saves/Jp/android')
|
||||
os.mkdir('Saves/fresh')
|
||||
os.mkdir('Saves/fresh/ios')
|
||||
os.mkdir('Saves/fresh/android')
|
||||
except:
|
||||
print(Fore.RED + Style.BRIGHT + 'Unable to create saves file')
|
||||
|
||||
while True:
|
||||
|
||||
# Decide which client to use.
|
||||
print(' ')
|
||||
print(Fore.CYAN + Style.BRIGHT + 'Choose a version')
|
||||
print('---------------------------------')
|
||||
print(' ')
|
||||
while True:
|
||||
print('Which version? (' + Fore.YELLOW + Style.BRIGHT + 'Jp: 1 ' + Style.RESET_ALL + 'or ' + Fore.YELLOW + Style.BRIGHT + 'Global: 2' + Style.RESET_ALL + ')',end='')
|
||||
client = input(" ")
|
||||
if client.lower() == '1':
|
||||
config.client = 'japan'
|
||||
while True:
|
||||
print(Fore.CYAN + Style.BRIGHT + 'Enter The BotZone ')
|
||||
print('---------------------------------')
|
||||
print("You're currently on JP")
|
||||
print('---------------------------------')
|
||||
print(Fore.CYAN + Style.BRIGHT + 'New Account :' + Fore.YELLOW + Style.BRIGHT + ' 0')
|
||||
print('---------------------------------')
|
||||
print(Fore.CYAN + Style.BRIGHT + 'Transfer Account :' + Fore.YELLOW + Style.BRIGHT + ' 1')
|
||||
print('---------------------------------')
|
||||
print(Fore.CYAN + Style.BRIGHT + 'Load From Save :' + Fore.YELLOW + Style.BRIGHT + ' 2')
|
||||
print('---------------------------------')
|
||||
print(Fore.CYAN + Style.BRIGHT + 'Daily Login :' + Fore.YELLOW + Style.BRIGHT + ' 3')
|
||||
print('---------------------------------')
|
||||
print(Fore.CYAN + Style.BRIGHT + 'Update database:' + Fore.YELLOW + Style.BRIGHT + ' 4')
|
||||
print('---------------------------------')
|
||||
print(Fore.CYAN + Style.BRIGHT + 'BotZone Discord Link:' + Fore.YELLOW + Style.BRIGHT + ' 5')
|
||||
print('---------------------------------')
|
||||
print(' ')
|
||||
command = input('Enter your choice: ')
|
||||
if command == '0':
|
||||
print(' ')
|
||||
config.identifier = commands.signup()
|
||||
commands.Jp_save_account()
|
||||
config.access_token, config.secret = commands.signin(config.identifier)
|
||||
commands.tutorial()
|
||||
commands.daily_login()
|
||||
break
|
||||
elif command == '1':
|
||||
print(' ')
|
||||
commands.Jp_transfer_account()
|
||||
commands.daily_login()
|
||||
break
|
||||
elif command == '2':
|
||||
print(' ')
|
||||
commands.Jp_load_account()
|
||||
commands.daily_login()
|
||||
commands.accept_gifts()
|
||||
commands.accept_missions()
|
||||
break
|
||||
elif command == '3':
|
||||
print('')
|
||||
commands.Jp_bulk_daily_logins()
|
||||
break
|
||||
elif command == '4':
|
||||
print('')
|
||||
commands.db_download()
|
||||
elif command == '5':
|
||||
webbrowser.open(commands.discordurl, new=0, autoraise=True)
|
||||
elif command == 'exit':
|
||||
exit()
|
||||
else:
|
||||
print(Fore.RED + Style.BRIGHT + "Command not understood")
|
||||
|
||||
# User commands.
|
||||
while True:
|
||||
print('---------------------------------')
|
||||
print(
|
||||
Fore.CYAN + Style.BRIGHT + "Type" + Fore.YELLOW + Style.BRIGHT + " 'help'" + Fore.CYAN + Style.BRIGHT + " to view all commands.")
|
||||
|
||||
# Set up comma separated chain commands. Handled via stdin
|
||||
try:
|
||||
command = input()
|
||||
except:
|
||||
sys.stdin = sys.__stdin__
|
||||
command = input()
|
||||
|
||||
if command == 'exit':
|
||||
break
|
||||
# Pass command to command executor and handle keyboard interrupts.
|
||||
try:
|
||||
commands.user_command_executor(command)
|
||||
except KeyboardInterrupt:
|
||||
print(Fore.CYAN + Style.BRIGHT + 'User interrupted process.')
|
||||
except Exception as e:
|
||||
print(Fore.RED + Style.BRIGHT + repr(e))
|
||||
break
|
||||
elif client.lower() == '2':
|
||||
config.client = 'global'
|
||||
print(' ')
|
||||
while True:
|
||||
print(Fore.CYAN + Style.BRIGHT + 'Enter The BotZone ')
|
||||
print('---------------------------------')
|
||||
print("You're currently on GLB")
|
||||
print('---------------------------------')
|
||||
print(Fore.CYAN + Style.BRIGHT + 'New Account :' + Fore.YELLOW + Style.BRIGHT + ' 0')
|
||||
print('---------------------------------')
|
||||
print(Fore.CYAN + Style.BRIGHT + 'Transfer Account :' + Fore.YELLOW + Style.BRIGHT + ' 1')
|
||||
print('---------------------------------')
|
||||
print(Fore.CYAN + Style.BRIGHT + 'Load From Save :' + Fore.YELLOW + Style.BRIGHT + ' 2')
|
||||
print('---------------------------------')
|
||||
print(Fore.CYAN + Style.BRIGHT + 'New Fresh Account :' + Fore.YELLOW + Style.BRIGHT + ' 3')
|
||||
print('---------------------------------')
|
||||
print(Fore.CYAN + Style.BRIGHT + 'Load Fresh Account :' + Fore.YELLOW + Style.BRIGHT + ' 4')
|
||||
print('---------------------------------')
|
||||
print(Fore.CYAN + Style.BRIGHT + 'Daily Login :' + Fore.YELLOW + Style.BRIGHT + ' 5')
|
||||
print('---------------------------------')
|
||||
print(Fore.CYAN + Style.BRIGHT + 'Update database:' + Fore.YELLOW + Style.BRIGHT + ' 6')
|
||||
print('---------------------------------')
|
||||
print(Fore.CYAN + Style.BRIGHT + 'BotZone Discord Link:' + Fore.YELLOW + Style.BRIGHT + ' 7')
|
||||
print('---------------------------------')
|
||||
command = input('Enter your choice: ')
|
||||
if command == '0':
|
||||
print(' ')
|
||||
config.identifier = commands.signup()
|
||||
commands.save_account()
|
||||
config.access_token, config.secret = commands.signin(config.identifier)
|
||||
commands.tutorial()
|
||||
commands.daily_login()
|
||||
break
|
||||
elif command == '1':
|
||||
print(' ')
|
||||
commands.transfer_account()
|
||||
commands.daily_login()
|
||||
break
|
||||
elif command == '2':
|
||||
print(' ')
|
||||
commands.load_account()
|
||||
commands.daily_login()
|
||||
commands.accept_gifts()
|
||||
commands.accept_missions()
|
||||
break
|
||||
elif command == '3':
|
||||
print(' ')
|
||||
config.identifier = commands.signup()
|
||||
commands.fresh_save_account()
|
||||
config.access_token, config.secret = commands.signin(config.identifier)
|
||||
commands.tutorial()
|
||||
commands.daily_login()
|
||||
break
|
||||
elif command == '4':
|
||||
print(' ')
|
||||
commands.fresh_load_account()
|
||||
commands.daily_login()
|
||||
commands.accept_gifts()
|
||||
commands.accept_missions()
|
||||
break
|
||||
elif command == '5':
|
||||
print('')
|
||||
commands.bulk_daily_logins()
|
||||
commands.fresh_bulk_daily_logins()
|
||||
elif command == '6':
|
||||
print('')
|
||||
commands.db_download()
|
||||
elif command == '7':
|
||||
webbrowser.open(commands.discordurl, new=0, autoraise=True)
|
||||
elif command == 'exit':
|
||||
exit()
|
||||
else:
|
||||
print(Fore.RED + Style.BRIGHT + "Command not understood")
|
||||
|
||||
# User commands.
|
||||
while True:
|
||||
print('---------------------------------')
|
||||
print(
|
||||
Fore.CYAN + Style.BRIGHT + "Type" + Fore.YELLOW + Style.BRIGHT + " 'help'" + Fore.CYAN + Style.BRIGHT + " to view all commands.")
|
||||
|
||||
# Set up comma separated chain commands. Handled via stdin
|
||||
try:
|
||||
command = input()
|
||||
except:
|
||||
sys.stdin = sys.__stdin__
|
||||
command = input()
|
||||
|
||||
if command == 'exit':
|
||||
break
|
||||
# Pass command to command executor and handle keyboard interrupts.
|
||||
try:
|
||||
commands.user_command_executor(command)
|
||||
except KeyboardInterrupt:
|
||||
print(Fore.CYAN + Style.BRIGHT + 'User interrupted process.')
|
||||
except Exception as e:
|
||||
print(Fore.RED + Style.BRIGHT + repr(e))
|
||||
|
||||
break
|
||||
else:
|
||||
print(Fore.RED + Style.BRIGHT + "Command not understood")
|
||||
400
BotZone2.8v1 Android/aa.py
Normal file
400
BotZone2.8v1 Android/aa.py
Normal file
|
|
@ -0,0 +1,400 @@
|
|||
import commands
|
||||
from colorama import init, Fore, Back, Style
|
||||
# Coloroma autoreset
|
||||
init(autoreset=True)
|
||||
|
||||
|
||||
def ss():
|
||||
print("TEQ Super Saiyan God SS Vegito")
|
||||
stage = input('What stage would you like to complete(519002 Sublime Blue!) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def sss():
|
||||
print("PHY Super Saiyan Broly")
|
||||
stage = input('What stage would you like to complete(548001 The Greatest Saiyan Adversary) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def ssss():
|
||||
print("STR Super Gogeta")
|
||||
stage = input('What stage would you like to complete(505003 Fusion Reborn!) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(2): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def s():
|
||||
print("AGL Super Saiyan Gogeta")
|
||||
stage = input('What stage would you like to complete(549001 The Omnipotent Saiyan Warrior) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def a():
|
||||
print("INT SSJ3 Bardock")
|
||||
stage = input('What stage would you like to complete(534001 The Unknown Battle : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def aa():
|
||||
print("STR SSJ4 Goku")
|
||||
stage = input('What stage would you like to complete(525001 The Scarlet Hero! Super Saiyan 4! : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def aaa():
|
||||
print("INT UI Goku")
|
||||
stage = input('What stage would you like to complete(538001 Kaboom! Ultra Instinct : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def aaaa():
|
||||
print("AGL SSJ4 Vegeta")
|
||||
stage = input('What stage would you like to complete(526001 The Crimson Flash! Super Saiyan 4 : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def b():
|
||||
print("PHY FP Frieza")
|
||||
stage = input('What stage would you like to complete(507002 Full-Power Final Battle : ')
|
||||
difficulty = input('Enter the difficulty|(2:Z-Hard): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def bb():
|
||||
print("TEQ Golden Frieza")
|
||||
stage = input('What stage would you like to complete(516001 Emperors Obsession Area : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def bbb():
|
||||
print("AGL SSJ3 Goku")
|
||||
stage = input('What stage would you like to complete(504002 Ultimate Finishing Move Area : ')
|
||||
difficulty = input('Enter the difficulty|(2:Z-Hard): ')
|
||||
loop = input('Enter how many times to execute(10): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def bbbb():
|
||||
print("TEQ SSJ4 Gogeta")
|
||||
stage = input('What stage would you like to complete(532001 The Ultimate Super Gogeta : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def c():
|
||||
print("INT Super Gogeta")
|
||||
stage = input('What stage would you like to complete(505003 Fusion Reborn!) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def cc():
|
||||
print("SSJ3 Gotenks")
|
||||
stage = input('What stage would you like to complete(513002 Super Gotenks) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
def ccc():
|
||||
print("TEQ FP SSJ4 Goku")
|
||||
stage = input('What stage would you like to complete(542001 Transcend Super Saiyan 4) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def cccc():
|
||||
print("STR Jiren")
|
||||
stage = input('What stage would you like to complete(540002 Confronting the Strongest of All Universes) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def d():
|
||||
print("INT Golden Frieza")
|
||||
stage = input('What stage would you like to complete(533002 The True Golden Frieza) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def dd():
|
||||
print("PHY Android 17")
|
||||
stage = input('What stage would you like to complete(543001 Superb Ranger) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def ddd():
|
||||
print("TEQ Hit")
|
||||
stage = input('What stage would you like to complete(547001 The Deadliest Assassin) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def dddd():
|
||||
print("AGL SSBE Vegeta")
|
||||
stage = input('What stage would you like to complete(524002 Battle for Honor and Pride) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def e():
|
||||
print("PHY Kid Buu")
|
||||
stage = input('What stage would you like to complete(524003 Regression to Evil) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def ee():
|
||||
print("INT Kid Buu")
|
||||
stage = input('What stage would you like to complete(524003 Regression to Evil) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def eee():
|
||||
print("TEQ SSJ3 Goku (Angel)")
|
||||
stage = input('What stage would you like to complete(528001 Mighty Warrior: 24-Hour Revival) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def eeee():
|
||||
print("PHY Goku Black")
|
||||
stage = input('What stage would you like to complete(518002 Dark Nightmare) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def f():
|
||||
print("INT Goku Black")
|
||||
stage = input('What stage would you like to complete(518003 Black Harbinger of Destruction) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def ff():
|
||||
print("TEQ SSG Goku")
|
||||
stage = input('What stage would you like to complete(549001 The Omnipotent Saiyan Warrior) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def ffx():
|
||||
print("STR SSG Vegeta")
|
||||
stage = input('What stage would you like to complete(549001 The Omnipotent Saiyan Warrior) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def fff():
|
||||
print("AGL SSGSS Goku")
|
||||
stage = input('What stage would you like to complete(514001 Ceaseless Combat) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(5): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def ffff():
|
||||
print("STR Toppo")
|
||||
stage = input('What stage would you like to complete(524002 Battle for Honor and Pride) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(5): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def g():
|
||||
print("STR Rose Goku Black")
|
||||
stage = input('What stage would you like to complete(520002 Searing Rose-Colored Fury) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def gg():
|
||||
print("PHY SSGSS Vegito")
|
||||
stage = input('What stage would you like to complete(519001 Fusion in Blue) : ')
|
||||
difficulty = input('Enter the difficulty|(3:Super): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def ggg():
|
||||
print("STR SSJ3 Goku")
|
||||
stage = input('What stage would you like to complete(504002 Ultimate Finishing Move Area : ')
|
||||
difficulty = input('Enter the difficulty|(2:Z-Hard): ')
|
||||
loop = input('Enter how many times to execute(10): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def gggx():
|
||||
print("TEQ SSJ3 Broly")
|
||||
stage = input('What stage would you like to complete(531001 All-Time Nastiest Evolution : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def gggg():
|
||||
print("AGL Transgoku")
|
||||
stage = input('What stage would you like to complete(544001 Ever-Evolving Power : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def h():
|
||||
print("STR SSJ3 Vegeta")
|
||||
stage = input('What stage would you like to complete(510002 The Most Powerful Blow : ')
|
||||
difficulty = input('Enter the difficulty|(2:Z-Hard): ')
|
||||
loop = input('Enter how many times to execute(10): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def hh():
|
||||
print("PHY SSJ3 Gotenks")
|
||||
stage = input('What stage would you like to complete(513003 One Powerful Super Fusion! : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def hhh():
|
||||
print("AGL Turles")
|
||||
stage = input('What stage would you like to complete(539001 Arrival of the Universe-Crusher! : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def hhhh():
|
||||
print("STR Janemba")
|
||||
stage = input('What stage would you like to complete(506003 Overwhelming Force of Evil! : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def j():
|
||||
print("INT Janemba")
|
||||
stage = input('What stage would you like to complete(506003 Overwhelming Force of Evil! : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(2): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def jj():
|
||||
print("TEQ TransFrieza")
|
||||
stage = input('What stage would you like to complete(545001 Ever-Evolving Evil : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(11): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
|
||||
#####################################################################################################################
|
||||
def jjj():
|
||||
print("AGL Broly")
|
||||
stage = input('What stage would you like to complete(548001 The Greatest Saiyan Adversary) : ')
|
||||
difficulty = input('Enter the difficulty|(4:Super2): ')
|
||||
loop = input('Enter how many times to execute(5): ')
|
||||
for i in range(int(loop)):
|
||||
commands.complete_stage(stage, difficulty)
|
||||
|
||||
84
BotZone2.8v1 Android/android-database-sqlcipher/Makefile
Normal file
84
BotZone2.8v1 Android/android-database-sqlcipher/Makefile
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
.POSIX:
|
||||
.PHONY: init clean distclean build-openssl build publish-local-snapshot \
|
||||
publish-local-release publish-remote-snapshot public-remote-release check
|
||||
GRADLE = ./gradlew
|
||||
|
||||
init:
|
||||
git submodule update --init
|
||||
|
||||
clean:
|
||||
$(GRADLE) clean
|
||||
|
||||
distclean:
|
||||
$(GRADLE) distclean
|
||||
|
||||
build-openssl:
|
||||
$(GRADLE) buildOpenSSL
|
||||
|
||||
check:
|
||||
$(GRADLE) check
|
||||
|
||||
build-debug: check
|
||||
$(GRADLE) android-database-sqlcipher:bundleDebugAar \
|
||||
-PdebugBuild=true
|
||||
|
||||
build-release: check
|
||||
$(GRADLE) android-database-sqlcipher:bundleReleaseAar \
|
||||
-PdebugBuild=false
|
||||
|
||||
publish-local-snapshot:
|
||||
@ $(collect-signing-info) \
|
||||
$(GRADLE) \
|
||||
-PpublishSnapshot=true \
|
||||
-PpublishLocal=true \
|
||||
-PsigningKeyId="$$gpgKeyId" \
|
||||
-PsigningKeyRingFile="$$gpgKeyRingFile" \
|
||||
-PsigningKeyPassword="$$gpgPassword" \
|
||||
uploadArchives
|
||||
|
||||
publish-local-release:
|
||||
@ $(collect-signing-info) \
|
||||
$(GRADLE) \
|
||||
-PpublishSnapshot=false \
|
||||
-PpublishLocal=true \
|
||||
-PsigningKeyId="$$gpgKeyId" \
|
||||
-PsigningKeyRingFile="$$gpgKeyRingFile" \
|
||||
-PsigningKeyPassword="$$gpgPassword" \
|
||||
uploadArchives
|
||||
|
||||
publish-remote-snapshot:
|
||||
@ $(collect-signing-info) \
|
||||
$(collect-nexus-info) \
|
||||
$(GRADLE) \
|
||||
-PpublishSnapshot=true \
|
||||
-PpublishLocal=false \
|
||||
-PsigningKeyId="$$gpgKeyId" \
|
||||
-PsigningKeyRingFile="$$gpgKeyRingFile" \
|
||||
-PsigningKeyPassword="$$gpgPassword" \
|
||||
-PnexusUsername="$$nexusUsername" \
|
||||
-PnexusPassword="$$nexusPassword" \
|
||||
uploadArchives
|
||||
|
||||
publish-remote-release:
|
||||
@ $(collect-signing-info) \
|
||||
$(collect-nexus-info) \
|
||||
$(GRADLE) \
|
||||
-PpublishSnapshot=false \
|
||||
-PpublishLocal=false \
|
||||
-PdebugBuild=false \
|
||||
-PsigningKeyId="$$gpgKeyId" \
|
||||
-PsigningKeyRingFile="$$gpgKeyRingFile" \
|
||||
-PsigningKeyPassword="$$gpgPassword" \
|
||||
-PnexusUsername="$$nexusUsername" \
|
||||
-PnexusPassword="$$nexusPassword" \
|
||||
uploadArchives
|
||||
|
||||
collect-nexus-info := \
|
||||
read -p "Enter Nexus username:" nexusUsername; \
|
||||
stty -echo; read -p "Enter Nexus password:" nexusPassword; stty echo;
|
||||
|
||||
collect-signing-info := \
|
||||
read -p "Enter GPG signing key id:" gpgKeyId; \
|
||||
read -p "Enter full path to GPG keyring file \
|
||||
(possibly ${HOME}/.gnupg/secring.gpg)" gpgKeyRingFile; \
|
||||
stty -echo; read -p "Enter GPG password:" gpgPassword; stty echo;
|
||||
131
BotZone2.8v1 Android/android-database-sqlcipher/README.md
Normal file
131
BotZone2.8v1 Android/android-database-sqlcipher/README.md
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
### Download Source and Binaries
|
||||
|
||||
The latest AAR binary package information can be [here](https://www.zetetic.net/sqlcipher/open-source), the source can be found [here](https://github.com/sqlcipher/android-database-sqlcipher).
|
||||
<p><a title="Latest version from Maven Central" href="https://maven-badges.herokuapp.com/maven-central/net.zetetic/android-database-sqlcipher"><img src="https://maven-badges.herokuapp.com/maven-central/net.zetetic/android-database-sqlcipher/badge.svg"></a></p>
|
||||
|
||||
### Compatibility
|
||||
|
||||
SQLCipher for Android runs on Android 4–Android 9, for `armeabi`, `armeabi-v7a`, `x86`, `x86_64`, and `arm64_v8a` architectures.
|
||||
|
||||
### Contributions
|
||||
|
||||
We welcome contributions, to contribute to SQLCipher for Android, a [contributor agreement](https://www.zetetic.net/contributions/) needs to be submitted. All submissions should be based on the `master` branch.
|
||||
|
||||
### An Illustrative Terminal Listing
|
||||
|
||||
A typical SQLite database in unencrypted, and visually parseable even as encoded text. The following example shows the difference between hexdumps of a standard SQLite database and one implementing SQLCipher.
|
||||
|
||||
```
|
||||
~ sjlombardo$ hexdump -C sqlite.db
|
||||
00000000 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 |SQLite format 3.|
|
||||
…
|
||||
000003c0 65 74 32 74 32 03 43 52 45 41 54 45 20 54 41 42 |et2t2.CREATE TAB|
|
||||
000003d0 4c 45 20 74 32 28 61 2c 62 29 24 01 06 17 11 11 |LE t2(a,b)$…..|
|
||||
…
|
||||
000007e0 20 74 68 65 20 73 68 6f 77 15 01 03 01 2f 01 6f | the show…./.o|
|
||||
000007f0 6e 65 20 66 6f 72 20 74 68 65 20 6d 6f 6e 65 79 |ne for the money|
|
||||
|
||||
~ $ sqlite3 sqlcipher.db
|
||||
sqlite> PRAGMA KEY=’test123′;
|
||||
sqlite> CREATE TABLE t1(a,b);
|
||||
sqlite> INSERT INTO t1(a,b) VALUES (‘one for the money’, ‘two for the show’);
|
||||
sqlite> .quit
|
||||
|
||||
~ $ hexdump -C sqlcipher.db
|
||||
00000000 84 d1 36 18 eb b5 82 90 c4 70 0d ee 43 cb 61 87 |.?6.?..?p.?C?a.|
|
||||
00000010 91 42 3c cd 55 24 ab c6 c4 1d c6 67 b4 e3 96 bb |.B?..?|
|
||||
00000bf0 8e 99 ee 28 23 43 ab a4 97 cd 63 42 8a 8e 7c c6 |..?(#C??.?cB..|?|
|
||||
|
||||
~ $ sqlite3 sqlcipher.db
|
||||
sqlite> SELECT * FROM t1;
|
||||
Error: file is encrypted or is not a database
|
||||
```
|
||||
(example courtesy of SQLCipher)
|
||||
|
||||
### Application Integration
|
||||
|
||||
You have a two main options for using SQLCipher for Android in your app:
|
||||
|
||||
- Using it with Room or other consumers of the `androidx.sqlite` API
|
||||
|
||||
- Using the native SQLCipher for Android classes
|
||||
|
||||
In both cases, you will need to add a dependency on `net.zetetic:android-database-sqlcipher`,
|
||||
such as having the following line in your module's `build.gradle` `dependencies`
|
||||
closure:
|
||||
|
||||
```gradle
|
||||
implementation 'net.zetetic:android-database-sqlcipher:4.2.0'
|
||||
```
|
||||
|
||||
(replacing `4.2.0` with the version you want)
|
||||
|
||||
<a title="Latest version from Maven Central" href="https://maven-badges.herokuapp.com/maven-central/net.zetetic/android-database-sqlcipher"><img src="https://maven-badges.herokuapp.com/maven-central/net.zetetic/android-database-sqlcipher/badge.svg"></a>
|
||||
|
||||
#### Using SQLCipher for Android With Room
|
||||
|
||||
SQLCipher for Android has a `SupportFactory` class in the `net.sqlcipher.database` package
|
||||
that can be used to configure Room to use SQLCipher for Android.
|
||||
|
||||
There are two `SupportFactory` constructors:
|
||||
|
||||
- Both take a `byte[]` to use as the passphrase (if you have a `char[]`, use
|
||||
`SQLiteDatabase.getBytes()` to get a suitable `byte[]` to use)
|
||||
|
||||
- One constructor has a second parameter: a `SQLiteDatabaseHook` that you can use
|
||||
for executing SQL statements before or after the passphrase is used to decrypt
|
||||
the database
|
||||
|
||||
Then, pass your `SupportFactory` to `openHelperFactory()` on your `RoomDatabase.Builder`:
|
||||
|
||||
```java
|
||||
final byte[] passphrase = SQLiteDatabase.getBytes(userEnteredPassphrase);
|
||||
final SupportFactory factory = new SupportFactory(passphrase);
|
||||
final SomeDatabase room = Room.databaseBuilder(activity, SomeDatabase.class, DB_NAME)
|
||||
.openHelperFactory(factory)
|
||||
.build();
|
||||
```
|
||||
|
||||
Now, Room will make all of its database requests using SQLCipher for Android instead
|
||||
of the framework copy of SQLCipher.
|
||||
|
||||
Note that `SupportFactory` should work with other consumers of the `androidx.sqlite` API;
|
||||
Room is merely a prominent example.
|
||||
|
||||
#### Using SQLCipher for Android's Native API
|
||||
|
||||
If you have existing SQLite code using classes like `SQLiteDatabase` and `SQLiteOpenHelper`,
|
||||
converting your code to use SQLCipher for Android mostly is a three-step process:
|
||||
|
||||
1. Replace all `android.database.sqlite.*` `import` statements with ones that
|
||||
use `net.sqlcipher.database.*` (e.g., convert `android.database.sqlite.SQLiteDatabase`
|
||||
to `net.sqlcipher.database.SQLiteDatabase`)
|
||||
|
||||
2. Before attempting to open a database, call `SQLiteDatabase.loadLibs()`, passing
|
||||
in a `Context` (e.g., add this to `onCreate()` of your `Application` subclass, using
|
||||
the `Application` itself as the `Context`)
|
||||
|
||||
3. When opening a database (e.g., `SQLiteDatabase.openOrCreateDatabase()`), pass
|
||||
in the passphrase as a `char[]` or `byte[]`
|
||||
|
||||
The rest of your code may not need any changes.
|
||||
|
||||
An article covering both integration of SQLCipher into an Android application as well as building the source can be found [here](https://www.zetetic.net/sqlcipher/sqlcipher-for-android/).
|
||||
|
||||
### Building
|
||||
|
||||
In order to build `android-database-sqlcipher` from source you will need both the Android SDK, Gradle, and the Android NDK. We currently recommend using Android NDK version `r15c`, however we plan to update to a newer NDK release when possible. To complete the `make` command, the `ANDROID_NDK_ROOT` environment variable must be defined which should point to your NDK root. Once you have cloned the repo, change directory into the root of the repository and run the following commands:
|
||||
|
||||
```
|
||||
# this only needs to be done once
|
||||
make init
|
||||
|
||||
# to build the source for debug:
|
||||
make build-debug
|
||||
# or for a release build:
|
||||
make build-release
|
||||
```
|
||||
|
||||
### License
|
||||
|
||||
The Android support libraries are licensed under Apache 2.0, in line with the Android OS code on which they are based. The SQLCipher code itself is licensed under a BSD-style license from Zetetic LLC. Finally, the original SQLite code itself is in the public domain.
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
http://sqlcipher.net
|
||||
|
||||
Copyright (c) 2010 Zetetic LLC
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the ZETETIC LLC nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
#! /usr/bin/env bash
|
||||
|
||||
MINIMUM_ANDROID_SDK_VERSION=$1
|
||||
MINIMUM_ANDROID_64_BIT_SDK_VERSION=$2
|
||||
OPENSSL=openssl-$3
|
||||
|
||||
(cd src/main/external/;
|
||||
gunzip -c ${OPENSSL}.tar.gz | tar xf -
|
||||
)
|
||||
|
||||
(cd src/main/external/${OPENSSL};
|
||||
|
||||
if [[ ! ${MINIMUM_ANDROID_SDK_VERSION} ]]; then
|
||||
echo "MINIMUM_ANDROID_SDK_VERSION was not provided, include and rerun"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! ${MINIMUM_ANDROID_64_BIT_SDK_VERSION} ]]; then
|
||||
echo "MINIMUM_ANDROID_64_BIT_SDK_VERSION was not provided, include and rerun"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! ${ANDROID_NDK_ROOT} ]]; then
|
||||
echo "ANDROID_NDK_ROOT environment variable not set, set and rerun"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NDK_TOOLCHAIN_VERSION=4.9
|
||||
ANDROID_LIB_ROOT=../android-libs
|
||||
ANDROID_TOOLCHAIN_DIR=/tmp/sqlcipher-android-toolchain
|
||||
OPENSSL_CONFIGURE_OPTIONS="-fPIC no-idea no-camellia \
|
||||
no-seed no-bf no-cast no-rc2 no-rc4 no-rc5 no-md2 \
|
||||
no-md4 no-ecdh no-sock no-ssl3 \
|
||||
no-dsa no-dh no-ec no-ecdsa no-tls1 \
|
||||
no-rfc3779 no-whirlpool no-srp \
|
||||
no-mdc2 no-ecdh no-engine \
|
||||
no-srtp"
|
||||
|
||||
HOST_INFO=`uname -a`
|
||||
case ${HOST_INFO} in
|
||||
Darwin*)
|
||||
TOOLCHAIN_SYSTEM=darwin-x86_64
|
||||
;;
|
||||
Linux*)
|
||||
if [[ "${HOST_INFO}" == *i686* ]]
|
||||
then
|
||||
TOOLCHAIN_SYSTEM=linux-x86
|
||||
else
|
||||
TOOLCHAIN_SYSTEM=linux-x86_64
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Toolchain unknown for host system"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
rm -rf ${ANDROID_LIB_ROOT}
|
||||
|
||||
for SQLCIPHER_TARGET_PLATFORM in armeabi armeabi-v7a x86 x86_64 arm64-v8a
|
||||
do
|
||||
echo "Building libcrypto.a for ${SQLCIPHER_TARGET_PLATFORM}"
|
||||
case "${SQLCIPHER_TARGET_PLATFORM}" in
|
||||
armeabi)
|
||||
TOOLCHAIN_ARCH=arm
|
||||
TOOLCHAIN_PREFIX=arm-linux-androideabi
|
||||
TOOLCHAIN_FOLDER=arm-linux-androideabi
|
||||
CONFIGURE_ARCH=android-arm
|
||||
ANDROID_API_VERSION=${MINIMUM_ANDROID_SDK_VERSION}
|
||||
OFFSET_BITS=32
|
||||
TOOLCHAIN_DIR=${ANDROID_TOOLCHAIN_DIR}-armeabi
|
||||
;;
|
||||
armeabi-v7a)
|
||||
TOOLCHAIN_ARCH=arm
|
||||
TOOLCHAIN_PREFIX=arm-linux-androideabi
|
||||
TOOLCHAIN_FOLDER=arm-linux-androideabi
|
||||
CONFIGURE_ARCH="android-arm -march=armv7-a"
|
||||
ANDROID_API_VERSION=${MINIMUM_ANDROID_SDK_VERSION}
|
||||
OFFSET_BITS=32
|
||||
TOOLCHAIN_DIR=${ANDROID_TOOLCHAIN_DIR}-armeabi-v7a
|
||||
;;
|
||||
x86)
|
||||
TOOLCHAIN_ARCH=x86
|
||||
TOOLCHAIN_PREFIX=i686-linux-android
|
||||
TOOLCHAIN_FOLDER=x86
|
||||
CONFIGURE_ARCH=android-x86
|
||||
ANDROID_API_VERSION=${MINIMUM_ANDROID_SDK_VERSION}
|
||||
OFFSET_BITS=32
|
||||
TOOLCHAIN_DIR=${ANDROID_TOOLCHAIN_DIR}-x86
|
||||
;;
|
||||
x86_64)
|
||||
TOOLCHAIN_ARCH=x86_64
|
||||
TOOLCHAIN_PREFIX=x86_64-linux-android
|
||||
TOOLCHAIN_FOLDER=x86_64
|
||||
CONFIGURE_ARCH=android64-x86_64
|
||||
ANDROID_API_VERSION=${MINIMUM_ANDROID_64_BIT_SDK_VERSION}
|
||||
OFFSET_BITS=64
|
||||
TOOLCHAIN_DIR=${ANDROID_TOOLCHAIN_DIR}-x86_64
|
||||
;;
|
||||
arm64-v8a)
|
||||
TOOLCHAIN_ARCH=arm64
|
||||
TOOLCHAIN_PREFIX=aarch64-linux-android
|
||||
TOOLCHAIN_FOLDER=aarch64-linux-android
|
||||
CONFIGURE_ARCH=android-arm64
|
||||
ANDROID_API_VERSION=${MINIMUM_ANDROID_64_BIT_SDK_VERSION}
|
||||
OFFSET_BITS=64
|
||||
TOOLCHAIN_DIR=${ANDROID_TOOLCHAIN_DIR}-arm64-v8a
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported build platform:${SQLCIPHER_TARGET_PLATFORM}"
|
||||
exit 1
|
||||
esac
|
||||
SOURCE_TOOLCHAIN_DIR=${ANDROID_NDK_ROOT}/toolchains/${TOOLCHAIN_FOLDER}-${NDK_TOOLCHAIN_VERSION}/prebuilt/${TOOLCHAIN_SYSTEM}
|
||||
rm -rf ${TOOLCHAIN_DIR}
|
||||
mkdir -p "${ANDROID_LIB_ROOT}/${SQLCIPHER_TARGET_PLATFORM}"
|
||||
python ${ANDROID_NDK_ROOT}/build/tools/make_standalone_toolchain.py \
|
||||
--arch ${TOOLCHAIN_ARCH} \
|
||||
--api ${ANDROID_API_VERSION} \
|
||||
--install-dir ${TOOLCHAIN_DIR} \
|
||||
--unified-headers
|
||||
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Error executing make_standalone_toolchain.py for ${TOOLCHAIN_ARCH}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export PATH=${TOOLCHAIN_DIR}/bin:${PATH}
|
||||
|
||||
ANDROID_NDK=${ANDROID_NDK_ROOT} \
|
||||
PATH=${SOURCE_TOOLCHAIN_DIR}/bin:${PATH} \
|
||||
./Configure ${CONFIGURE_ARCH} \
|
||||
-D__ANDROID_API__=${ANDROID_API_VERSION} \
|
||||
-D_FILE_OFFSET_BITS=${OFFSET_BITS} \
|
||||
${OPENSSL_CONFIGURE_OPTIONS} \
|
||||
--sysroot=${TOOLCHAIN_DIR}/sysroot
|
||||
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Error executing:./Configure ${CONFIGURE_ARCH} ${OPENSSL_CONFIGURE_OPTIONS}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
make clean
|
||||
make build_libs
|
||||
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Error executing make for platform:${SQLCIPHER_TARGET_PLATFORM}"
|
||||
exit 1
|
||||
fi
|
||||
mv libcrypto.a ${ANDROID_LIB_ROOT}/${SQLCIPHER_TARGET_PLATFORM}
|
||||
done
|
||||
)
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
apply plugin: "com.android.library"
|
||||
apply plugin: "org.ec4j.editorconfig"
|
||||
apply from: "native.gradle"
|
||||
apply from: "maven.gradle"
|
||||
|
||||
android {
|
||||
|
||||
compileSdkVersion "${compileAndroidSdkVersion}" as Integer
|
||||
|
||||
defaultConfig {
|
||||
versionName "${clientVersionNumber}"
|
||||
minSdkVersion "${minimumAndroidSdkVersion}"
|
||||
targetSdkVersion "${targetAndroidSdkVersion}"
|
||||
versionCode 1
|
||||
versionName "${clientVersionNumber}"
|
||||
archivesBaseName = "${archivesBaseName}-${versionName}"
|
||||
}
|
||||
|
||||
editorconfig {
|
||||
includes = ["src/**", "*.gradle"]
|
||||
excludes = ["src/main/external/sqlcipher/**", "src/main/external/openssl-*/**"]
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
debuggable true
|
||||
}
|
||||
release {
|
||||
debuggable false
|
||||
minifyEnabled false
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
jniLibs.srcDirs "${rootProject.ext.nativeRootOutputDir}/libs"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "androidx.sqlite:sqlite:2.0.1"
|
||||
}
|
||||
|
||||
clean.dependsOn cleanNative
|
||||
check.dependsOn editorconfigCheck
|
||||
buildNative.mustRunAfter buildAmalgamation
|
||||
buildAmalgamation.mustRunAfter buildOpenSSL
|
||||
preBuild.dependsOn([buildOpenSSL, buildAmalgamation, buildNative])
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
apply plugin: "maven"
|
||||
apply plugin: "signing"
|
||||
import org.gradle.plugins.signing.Sign
|
||||
|
||||
def isReleaseBuild() {
|
||||
return mavenVersionName.contains("SNAPSHOT") == false
|
||||
}
|
||||
|
||||
def getReleaseRepositoryUrl() {
|
||||
return hasProperty('mavenReleaseRepositoryUrl') ? mavenReleaseRepositoryUrl
|
||||
: "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
|
||||
}
|
||||
|
||||
def getSnapshotRepositoryUrl() {
|
||||
if(hasProperty('mavenLocalRepositoryPrefix')) {
|
||||
return "${mavenLocalRepositoryPrefix}${buildDir}/${mavenSnapshotRepositoryUrl}"
|
||||
} else {
|
||||
return hasProperty('mavenSnapshotRepositoryUrl') ? mavenSnapshotRepositoryUrl
|
||||
: "https://oss.sonatype.org/content/repositories/snapshots/"
|
||||
}
|
||||
}
|
||||
|
||||
def getRepositoryUsername() {
|
||||
return hasProperty('nexusUsername') ? nexusUsername : ""
|
||||
}
|
||||
|
||||
def getRepositoryPassword() {
|
||||
return hasProperty('nexusPassword') ? nexusPassword : ""
|
||||
}
|
||||
|
||||
gradle.taskGraph.whenReady { taskGraph ->
|
||||
if (taskGraph.allTasks.any { it instanceof Sign }) {
|
||||
allprojects { ext."signing.keyId" = "${signingKeyId}" }
|
||||
allprojects { ext."signing.secretKeyRingFile" = "${signingKeyRingFile}" }
|
||||
allprojects { ext."signing.password" = "${signingKeyPassword}" }
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate { project ->
|
||||
uploadArchives {
|
||||
repositories {
|
||||
mavenDeployer {
|
||||
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
|
||||
|
||||
pom.groupId = mavenGroup
|
||||
pom.artifactId = mavenArtifactId
|
||||
pom.version = mavenVersionName
|
||||
|
||||
repository(url: getReleaseRepositoryUrl()) {
|
||||
authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
|
||||
}
|
||||
snapshotRepository(url: getSnapshotRepositoryUrl()) {
|
||||
authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
|
||||
}
|
||||
|
||||
pom.project {
|
||||
name mavenArtifactId
|
||||
packaging mavenPackaging
|
||||
description mavenPomDescription
|
||||
url mavenPomUrl
|
||||
|
||||
scm {
|
||||
url mavenScmUrl
|
||||
connection mavenScmConnection
|
||||
developerConnection mavenScmDeveloperConnection
|
||||
}
|
||||
|
||||
licenses {
|
||||
license {
|
||||
url mavenLicenseUrl
|
||||
}
|
||||
}
|
||||
|
||||
developers {
|
||||
developer {
|
||||
name mavenDeveloperName
|
||||
email mavenDeveloperEmail
|
||||
organization mavenDeveloperOrganization
|
||||
organizationUrl mavenDeveloperUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signing {
|
||||
required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") }
|
||||
sign configurations.archives
|
||||
}
|
||||
|
||||
task androidSourcesJar(type: Jar) {
|
||||
classifier = "sources"
|
||||
from android.sourceSets.main.java.sourceFiles
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives androidSourcesJar
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
import org.gradle.internal.logging.text.StyledTextOutputFactory
|
||||
import static org.gradle.internal.logging.text.StyledTextOutput.Style
|
||||
|
||||
task buildOpenSSL() {
|
||||
onlyIf {
|
||||
def armNativeFile = new File("${androidNativeRootDir}/armeabi/libcrypto.a")
|
||||
if (armNativeFile.exists()) {
|
||||
def out = services.get(StyledTextOutputFactory).create("")
|
||||
out.style(Style.Normal).text("${androidNativeRootDir}/armeabi/libcrypto.a exists").style(Style.Info).println(' SKIPPED')
|
||||
}
|
||||
return !armNativeFile.exists()
|
||||
}
|
||||
doLast {
|
||||
exec {
|
||||
workingDir "${projectDir}"
|
||||
commandLine "./build-openssl-libraries.sh",
|
||||
"${minimumAndroidSdkVersion}",
|
||||
"${minimumAndroid64BitSdkVersion}",
|
||||
"${opensslVersion}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task buildAmalgamation() {
|
||||
onlyIf {
|
||||
def amalgamation = new File("${projectDir}/src/main/external/sqlcipher/sqlite3.c")
|
||||
return !amalgamation.exists()
|
||||
}
|
||||
doLast {
|
||||
exec {
|
||||
workingDir "${projectDir}/src/main/external/sqlcipher"
|
||||
environment("CFLAGS", "${sqlcipherCFlags}")
|
||||
commandLine "./configure", "--enable-tempstore=yes", "--with-crypto-lib=none"
|
||||
}
|
||||
exec {
|
||||
workingDir "${projectDir}/src/main/external/sqlcipher"
|
||||
environment("CFLAGS", "${sqlcipherCFlags}")
|
||||
commandLine "make", "sqlite3.c"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task buildNative() {
|
||||
description "Build the native SQLCipher binaries"
|
||||
doLast {
|
||||
executeNdkBuild(
|
||||
"${nativeRootOutputDir}/libs32",
|
||||
file("src/main/cpp").absolutePath,
|
||||
file("src/main/cpp/Application32.mk").absolutePath,
|
||||
"${sqlcipherCFlags}", "${otherSqlcipherCFlags}",
|
||||
"${minimumAndroidSdkVersion}")
|
||||
executeNdkBuild(
|
||||
"${nativeRootOutputDir}/libs64",
|
||||
file("src/main/cpp").absolutePath,
|
||||
file("src/main/cpp/Application64.mk").absolutePath,
|
||||
"${sqlcipherCFlags}", "${otherSqlcipherCFlags}",
|
||||
"${minimumAndroid64BitSdkVersion}")
|
||||
exec {
|
||||
workingDir "${nativeRootOutputDir}"
|
||||
commandLine "mkdir", "-p", "libs"
|
||||
}
|
||||
copy {
|
||||
from fileTree("${nativeRootOutputDir}/libs32").include("*/*")
|
||||
into "${nativeRootOutputDir}/libs"
|
||||
from fileTree("${nativeRootOutputDir}/libs64").include("*/*")
|
||||
into "${nativeRootOutputDir}/libs"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task cleanOpenSSL() {
|
||||
description "Clean the OpenSSL source"
|
||||
doLast {
|
||||
logger.info "Cleaning OpenSSL source"
|
||||
File file = new File("${opensslDir}")
|
||||
if (file.exists()) {
|
||||
file.deleteDir()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task cleanSQLCipher() {
|
||||
description "Clean the SQLCipher source"
|
||||
doLast {
|
||||
logger.info "Cleaning SQLCipher source"
|
||||
gitClean("${sqlcipherDir}")
|
||||
File amalgamationSource = new File("${sqlcipherDir}/sqlite3.c")
|
||||
File amalgamationHeader = new File("${sqlcipherDir}/sqlite3.h")
|
||||
if (amalgamationSource.exists()) amalgamationSource.delete()
|
||||
if (amalgamationHeader.exists()) amalgamationHeader.delete()
|
||||
}
|
||||
}
|
||||
|
||||
task cleanNative() {
|
||||
description "Clean the native (JNI) build artifacts"
|
||||
doLast {
|
||||
logger.info "Cleaning native build artifacts"
|
||||
["libs", "libs32", "libs64", "obj"].each {
|
||||
File file = new File("${projectDir}/src/main/${it}")
|
||||
if (file.exists()) {
|
||||
file.deleteDir()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task distclean(dependsOn: [clean, cleanSQLCipher, cleanOpenSSL]) {
|
||||
description "Clean build, SQLCipher, and OpenSSL artifacts"
|
||||
doLast {
|
||||
new File("${androidNativeRootDir}/").deleteDir()
|
||||
}
|
||||
}
|
||||
|
||||
def gitClean(directory) {
|
||||
logger.info "Cleaning directory:${directory}"
|
||||
exec {
|
||||
workingDir "${directory}"
|
||||
commandLine "git", "checkout", "-f"
|
||||
}
|
||||
exec {
|
||||
workingDir "${directory}"
|
||||
commandLine "git", "clean", "-d", "-f"
|
||||
}
|
||||
}
|
||||
|
||||
def executeNdkBuild(outputDir, androidMkDirectory, applicationMkFile,
|
||||
cflags, otherSqlcipherCFlags, androidVersion) {
|
||||
logger.info "Executing NDK build command"
|
||||
|
||||
def out = services.get(StyledTextOutputFactory).create("")
|
||||
out.style(Style.Normal).text("SQLCIPHER_CFLAGS=").style(Style.Info).println("${cflags}")
|
||||
out.style(Style.Normal).text("OPENSSL_DIR=").style(Style.Info).println("${opensslDir}")
|
||||
out.style(Style.Normal).text("SQLCIPHER_DIR=").style(Style.Info).println("${sqlcipherDir}")
|
||||
out.style(Style.Normal).text("SQLCIPHER_OTHER_CFLAGS=").style(Style.Info).println("${otherSqlcipherCFlags}")
|
||||
out.style(Style.Normal).text("ANDROID_NATIVE_ROOT_DIR=").style(Style.Info).println("${androidNativeRootDir}")
|
||||
out.style(Style.Normal).text("NDK_APP_PLATFORM=").style(Style.Info).println("${androidVersion}")
|
||||
|
||||
exec {
|
||||
def outputDirectory = "NDK_LIBS_OUT=${outputDir}"
|
||||
def applicationFile = "NDK_APPLICATION_MK=${applicationMkFile}"
|
||||
def environmentVariables = ["SQLCIPHER_CFLAGS" : "${cflags}",
|
||||
"OPENSSL_DIR" : "${opensslDir}",
|
||||
"SQLCIPHER_DIR" : "${sqlcipherDir}",
|
||||
"SQLCIPHER_OTHER_CFLAGS" : "${otherSqlcipherCFlags}",
|
||||
"ANDROID_NATIVE_ROOT_DIR": "${androidNativeRootDir}",
|
||||
"NDK_APP_PLATFORM" : "${androidVersion}"]
|
||||
environment(environmentVariables)
|
||||
commandLine "ndk-build", "${ndkBuildType}",
|
||||
"--environment-overrides", outputDirectory,
|
||||
"-C", androidMkDirectory, applicationFile
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="net.sqlcipher">
|
||||
</manifest>
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
**
|
||||
** Copyright 2007, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
interface IContentObserver
|
||||
{
|
||||
/**
|
||||
* This method is called when an update occurs to the cursor that is being
|
||||
* observed. selfUpdate is true if the update was caused by a call to
|
||||
* commit on the cursor that is being observed.
|
||||
*/
|
||||
oneway void onChange(boolean selfUpdate);
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
LOCAL_PATH := $(call my-dir)
|
||||
MY_PATH := $(LOCAL_PATH)
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_PATH := $(MY_PATH)
|
||||
|
||||
SQLCIPHER_SRC := $(SQLCIPHER_DIR)/sqlite3.c
|
||||
LOCAL_CFLAGS += $(SQLCIPHER_CFLAGS) $(SQLCIPHER_OTHER_CFLAGS)
|
||||
LOCAL_C_INCLUDES := $(SQLCIPHER_DIR) $(LOCAL_PATH)
|
||||
LOCAL_LDLIBS := -llog -latomic
|
||||
LOCAL_LDFLAGS += -L$(ANDROID_NATIVE_ROOT_DIR)/$(TARGET_ARCH_ABI) -fuse-ld=bfd
|
||||
LOCAL_STATIC_LIBRARIES += static-libcrypto
|
||||
LOCAL_MODULE := libsqlcipher
|
||||
LOCAL_SRC_FILES := $(SQLCIPHER_SRC) \
|
||||
jni_exception.cpp \
|
||||
net_sqlcipher_database_SQLiteCompiledSql.cpp \
|
||||
net_sqlcipher_database_SQLiteDatabase.cpp \
|
||||
net_sqlcipher_database_SQLiteProgram.cpp \
|
||||
net_sqlcipher_database_SQLiteQuery.cpp \
|
||||
net_sqlcipher_database_SQLiteStatement.cpp \
|
||||
net_sqlcipher_CursorWindow.cpp \
|
||||
CursorWindow.cpp
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := static-libcrypto
|
||||
LOCAL_EXPORT_C_INCLUDES := $(OPENSSL_DIR)/include
|
||||
LOCAL_SRC_FILES := $(ANDROID_NATIVE_ROOT_DIR)/$(TARGET_ARCH_ABI)/libcrypto.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
APP_PROJECT_PATH := $(shell pwd)
|
||||
APP_ABI := armeabi armeabi-v7a x86
|
||||
APP_PLATFORM := android-$(NDK_APP_PLATFORM)
|
||||
APP_BUILD_SCRIPT := $(APP_PROJECT_PATH)/Android.mk
|
||||
APP_STL := stlport_static
|
||||
APP_CFLAGS := -D_FILE_OFFSET_BITS=32
|
||||
APP_LDFLAGS += -Wl,--exclude-libs,ALL
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
APP_PROJECT_PATH := $(shell pwd)
|
||||
APP_ABI := x86_64 arm64-v8a
|
||||
APP_PLATFORM := android-$(NDK_APP_PLATFORM)
|
||||
APP_BUILD_SCRIPT := $(APP_PROJECT_PATH)/Android.mk
|
||||
APP_STL := stlport_static
|
||||
APP_CFLAGS := -D_FILE_OFFSET_BITS=64
|
||||
APP_LDFLAGS += -Wl,--exclude-libs,ALL
|
||||
|
|
@ -0,0 +1,406 @@
|
|||
/*
|
||||
* Copyright (C) 2006-2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "CursorWindow"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <jni.h>
|
||||
#include "CursorWindow.h"
|
||||
|
||||
namespace sqlcipher {
|
||||
|
||||
CursorWindow::CursorWindow(size_t initialSize, size_t growthPaddingSize, size_t maxSize)
|
||||
{
|
||||
mInitialSize = initialSize;
|
||||
mGrowthPaddingSize = growthPaddingSize;
|
||||
mMaxSize = maxSize;
|
||||
LOG_WINDOW("CursorWindow::CursorWindow initialSize:%d growBySize:%d maxSize:%d\n",
|
||||
initialSize, growthPaddingSize, maxSize);
|
||||
}
|
||||
|
||||
bool CursorWindow::initBuffer(bool localOnly)
|
||||
{
|
||||
void* data = malloc(mInitialSize);
|
||||
if(data){
|
||||
mData = (uint8_t *) data;
|
||||
mHeader = (window_header_t *) mData;
|
||||
mSize = mInitialSize;
|
||||
clear();
|
||||
LOG_WINDOW("Created CursorWindow with new MemoryDealer: mFreeOffset = %d, mSize = %d, mInitialSize = %d, mGrowthPaddingSize = %d, mMaxSize = %d, mData = %p\n",
|
||||
mFreeOffset, mSize, mInitialSize, mGrowthPaddingSize, mMaxSize, mData);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CursorWindow::~CursorWindow()
|
||||
{
|
||||
if(mData){
|
||||
free(mData);
|
||||
}
|
||||
}
|
||||
|
||||
void CursorWindow::clear()
|
||||
{
|
||||
mHeader->numRows = 0;
|
||||
mHeader->numColumns = 0;
|
||||
mFreeOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE;
|
||||
// Mark the first chunk's next 'pointer' as null
|
||||
*((uint32_t *)(mData + mFreeOffset - sizeof(uint32_t))) = 0;
|
||||
mChunkNumToNextChunkOffset.clear();
|
||||
mLastChunkPtrOffset = 0;
|
||||
}
|
||||
|
||||
int32_t CursorWindow::freeSpace()
|
||||
{
|
||||
int32_t freeSpace = mSize - mFreeOffset;
|
||||
if (freeSpace < 0) {
|
||||
freeSpace = 0;
|
||||
}
|
||||
return freeSpace;
|
||||
}
|
||||
|
||||
field_slot_t * CursorWindow::allocRow()
|
||||
{
|
||||
// Fill in the row slot
|
||||
row_slot_t * rowSlot = allocRowSlot();
|
||||
if (rowSlot == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Record the original offset of the rowSlot prior to allocation of the field directory
|
||||
uint32_t rowSlotOffset = (uint8_t*)rowSlot - mData;
|
||||
|
||||
// Allocate the slots for the field directory
|
||||
size_t fieldDirSize = mHeader->numColumns * sizeof(field_slot_t);
|
||||
uint32_t fieldDirOffset = alloc(fieldDirSize);
|
||||
if (!fieldDirOffset) {
|
||||
mHeader->numRows--;
|
||||
LOGE("The row failed, so back out the new row accounting from allocRowSlot %d", mHeader->numRows);
|
||||
return NULL;
|
||||
}
|
||||
field_slot_t * fieldDir = (field_slot_t *)offsetToPtr(fieldDirOffset);
|
||||
memset(fieldDir, 0x0, fieldDirSize);
|
||||
|
||||
// Reset the rowSlot pointer relative to mData
|
||||
// If the last alloc relocated mData this will be rowSlot's new address, otherwise the value will not change
|
||||
rowSlot = (row_slot_t*)(mData + rowSlotOffset);
|
||||
|
||||
LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n", (mHeader->numRows - 1), ((uint8_t *)rowSlot) - mData, fieldDirSize, fieldDirOffset);
|
||||
rowSlot->offset = fieldDirOffset;
|
||||
|
||||
return fieldDir;
|
||||
}
|
||||
|
||||
uint32_t CursorWindow::alloc(size_t requestedSize, bool aligned)
|
||||
{
|
||||
size_t size = 0, new_allocation_sz = 0;
|
||||
uint32_t padding;
|
||||
void *tempData = NULL;
|
||||
if (aligned) {
|
||||
// 4 byte alignment
|
||||
padding = 4 - (mFreeOffset & 0x3);
|
||||
} else {
|
||||
padding = 0;
|
||||
}
|
||||
size = requestedSize + padding;
|
||||
if (size > freeSpace()) {
|
||||
new_allocation_sz = mSize + size - freeSpace() + mGrowthPaddingSize;
|
||||
LOGE("need to grow: mSize = %d, size = %d, freeSpace() = %d, numRows = %d new_allocation_sz:%d\n",
|
||||
mSize, size, freeSpace(), mHeader->numRows, new_allocation_sz);
|
||||
if(mMaxSize == 0 || new_allocation_sz <= mMaxSize) {
|
||||
tempData = realloc((void *)mData, new_allocation_sz);
|
||||
if(tempData == NULL) return 0;
|
||||
mData = (uint8_t *)tempData;
|
||||
mHeader = (window_header_t *)mData;
|
||||
LOGE("allocation grew to:%d", new_allocation_sz);
|
||||
mSize = new_allocation_sz;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
uint32_t offset = mFreeOffset + padding;
|
||||
mFreeOffset += size;
|
||||
return offset;
|
||||
}
|
||||
|
||||
row_slot_t * CursorWindow::getRowSlot(int row)
|
||||
{
|
||||
LOG_WINDOW("getRowSlot entered: requesting row:%d, current row num:%d", row, mHeader->numRows);
|
||||
unordered_map<int, uint32_t>::iterator result;
|
||||
int chunkNum = row / ROW_SLOT_CHUNK_NUM_ROWS;
|
||||
int chunkPos = row % ROW_SLOT_CHUNK_NUM_ROWS;
|
||||
int chunkPtrOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t);
|
||||
uint8_t * rowChunk = mData + sizeof(window_header_t);
|
||||
|
||||
// check for chunkNum in cache
|
||||
result = mChunkNumToNextChunkOffset.find(chunkNum);
|
||||
if(result != mChunkNumToNextChunkOffset.end()){
|
||||
rowChunk = offsetToPtr(result->second);
|
||||
LOG_WINDOW("Retrieved chunk offset from cache for row:%d", row);
|
||||
return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t)));
|
||||
}
|
||||
|
||||
// walk the list, this shouldn't occur
|
||||
LOG_WINDOW("getRowSlot walking list %d times to find rowslot for row:%d", chunkNum, row);
|
||||
for (int i = 0; i < chunkNum; i++) {
|
||||
rowChunk = offsetToPtr(*((uint32_t *)(mData + chunkPtrOffset)));
|
||||
chunkPtrOffset = rowChunk - mData + (ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t));
|
||||
}
|
||||
return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t)));
|
||||
LOG_WINDOW("exit getRowSlot current row num %d, this row %d", mHeader->numRows, row);
|
||||
}
|
||||
|
||||
row_slot_t * CursorWindow::allocRowSlot()
|
||||
{
|
||||
int chunkNum = mHeader->numRows / ROW_SLOT_CHUNK_NUM_ROWS;
|
||||
int chunkPos = mHeader->numRows % ROW_SLOT_CHUNK_NUM_ROWS;
|
||||
int chunkPtrOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t);
|
||||
uint8_t * rowChunk = mData + sizeof(window_header_t);
|
||||
LOG_WINDOW("allocRowSlot entered: Allocating row slot, mHeader->numRows is %d, chunkNum is %d, chunkPos is %d",
|
||||
mHeader->numRows, chunkNum, chunkPos);
|
||||
|
||||
if(mLastChunkPtrOffset != 0){
|
||||
chunkPtrOffset = mLastChunkPtrOffset;
|
||||
}
|
||||
if(chunkNum > 0) {
|
||||
uint32_t nextChunkOffset = *((uint32_t *)(mData + chunkPtrOffset));
|
||||
LOG_WINDOW("nextChunkOffset is %d", nextChunkOffset);
|
||||
if (nextChunkOffset == 0) {
|
||||
mLastChunkPtrOffset = chunkPtrOffset;
|
||||
// Allocate a new row chunk
|
||||
nextChunkOffset = alloc(ROW_SLOT_CHUNK_SIZE, true);
|
||||
mChunkNumToNextChunkOffset.insert(make_pair(chunkNum, nextChunkOffset));
|
||||
if (nextChunkOffset == 0) {
|
||||
return NULL;
|
||||
}
|
||||
rowChunk = offsetToPtr(nextChunkOffset);
|
||||
LOG_WINDOW("allocated new chunk at %d, rowChunk = %p", nextChunkOffset, rowChunk);
|
||||
*((uint32_t *)(mData + chunkPtrOffset)) = rowChunk - mData;
|
||||
// Mark the new chunk's next 'pointer' as null
|
||||
*((uint32_t *)(rowChunk + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t))) = 0;
|
||||
} else {
|
||||
LOG_WINDOW("follwing 'pointer' to next chunk, offset of next pointer is %d", chunkPtrOffset);
|
||||
rowChunk = offsetToPtr(nextChunkOffset);
|
||||
chunkPtrOffset = rowChunk - mData + (ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t));
|
||||
if(chunkPos == ROW_SLOT_CHUNK_NUM_ROWS - 1){
|
||||
// prepare to allocate new rowslot_t now at end of row
|
||||
mLastChunkPtrOffset = chunkPtrOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
mHeader->numRows++;
|
||||
return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t)));
|
||||
}
|
||||
|
||||
field_slot_t * CursorWindow::getFieldSlotWithCheck(int row, int column)
|
||||
{
|
||||
LOG_WINDOW("getFieldSlotWithCheck entered: row:%d column:%d", row, column);
|
||||
if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) {
|
||||
LOGE("Bad request for field slot %d,%d. numRows = %d, numColumns = %d", row, column, mHeader->numRows, mHeader->numColumns);
|
||||
return NULL;
|
||||
}
|
||||
row_slot_t * rowSlot = getRowSlot(row);
|
||||
if (!rowSlot) {
|
||||
LOGE("Failed to find rowSlot for row %d", row);
|
||||
return NULL;
|
||||
}
|
||||
if (rowSlot->offset == 0 || rowSlot->offset >= mSize) {
|
||||
LOGE("Invalid rowSlot, offset = %d", rowSlot->offset);
|
||||
return NULL;
|
||||
}
|
||||
int fieldDirOffset = rowSlot->offset;
|
||||
return ((field_slot_t *)offsetToPtr(fieldDirOffset)) + column;
|
||||
}
|
||||
|
||||
uint32_t CursorWindow::read_field_slot(int row, int column, field_slot_t * slotOut)
|
||||
{
|
||||
LOG_WINDOW("read_field_slot entered: row:%d, column:%d, slotOut:%p", row, column, slotOut);
|
||||
if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) {
|
||||
LOGE("Bad request for field slot %d,%d. numRows = %d, numColumns = %d", row, column, mHeader->numRows, mHeader->numColumns);
|
||||
return -1;
|
||||
}
|
||||
row_slot_t * rowSlot = getRowSlot(row);
|
||||
if (!rowSlot) {
|
||||
LOGE("Failed to find rowSlot for row %d", row);
|
||||
return -1;
|
||||
}
|
||||
if (rowSlot->offset == 0 || rowSlot->offset >= mSize) {
|
||||
LOGE("Invalid rowSlot, offset = %d", rowSlot->offset);
|
||||
return -1;
|
||||
}
|
||||
LOG_WINDOW("Found field directory for %d,%d at rowSlot %d, offset %d", row, column, (uint8_t *)rowSlot - mData, rowSlot->offset);
|
||||
field_slot_t * fieldDir = (field_slot_t *)offsetToPtr(rowSlot->offset);
|
||||
LOG_WINDOW("Read field_slot_t %d,%d: offset = %d, size = %d, type = %d", row, column, fieldDir[column].data.buffer.offset, fieldDir[column].data.buffer.size, fieldDir[column].type);
|
||||
|
||||
// Copy the data to the out param
|
||||
slotOut->data.buffer.offset = fieldDir[column].data.buffer.offset;
|
||||
slotOut->data.buffer.size = fieldDir[column].data.buffer.size;
|
||||
slotOut->type = fieldDir[column].type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CursorWindow::copyIn(uint32_t offset, uint8_t const * data, size_t size)
|
||||
{
|
||||
assert(offset + size <= mSize);
|
||||
memcpy(mData + offset, data, size);
|
||||
}
|
||||
|
||||
void CursorWindow::copyIn(uint32_t offset, int64_t data)
|
||||
{
|
||||
assert(offset + sizeof(int64_t) <= mSize);
|
||||
memcpy(mData + offset, (uint8_t *)&data, sizeof(int64_t));
|
||||
}
|
||||
|
||||
void CursorWindow::copyIn(uint32_t offset, double data)
|
||||
{
|
||||
assert(offset + sizeof(double) <= mSize);
|
||||
memcpy(mData + offset, (uint8_t *)&data, sizeof(double));
|
||||
}
|
||||
|
||||
void CursorWindow::copyOut(uint32_t offset, uint8_t * data, size_t size)
|
||||
{
|
||||
assert(offset + size <= mSize);
|
||||
memcpy(data, mData + offset, size);
|
||||
}
|
||||
|
||||
int64_t CursorWindow::copyOutLong(uint32_t offset)
|
||||
{
|
||||
int64_t value;
|
||||
assert(offset + sizeof(int64_t) <= mSize);
|
||||
memcpy(&value, mData + offset, sizeof(int64_t));
|
||||
return value;
|
||||
}
|
||||
|
||||
double CursorWindow::copyOutDouble(uint32_t offset)
|
||||
{
|
||||
double value;
|
||||
assert(offset + sizeof(double) <= mSize);
|
||||
memcpy(&value, mData + offset, sizeof(double));
|
||||
return value;
|
||||
}
|
||||
|
||||
bool CursorWindow::putLong(unsigned int row, unsigned int col, int64_t value)
|
||||
{
|
||||
field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
|
||||
if (!fieldSlot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if WINDOW_STORAGE_INLINE_NUMERICS
|
||||
fieldSlot->data.l = value;
|
||||
#else
|
||||
int offset = alloc(sizeof(int64_t));
|
||||
if (!offset) {
|
||||
return false;
|
||||
}
|
||||
|
||||
copyIn(offset, value);
|
||||
|
||||
fieldSlot->data.buffer.offset = offset;
|
||||
fieldSlot->data.buffer.size = sizeof(int64_t);
|
||||
#endif
|
||||
fieldSlot->type = FIELD_TYPE_INTEGER;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CursorWindow::putDouble(unsigned int row, unsigned int col, double value)
|
||||
{
|
||||
field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
|
||||
if (!fieldSlot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if WINDOW_STORAGE_INLINE_NUMERICS
|
||||
fieldSlot->data.d = value;
|
||||
#else
|
||||
int offset = alloc(sizeof(int64_t));
|
||||
if (!offset) {
|
||||
return false;
|
||||
}
|
||||
|
||||
copyIn(offset, value);
|
||||
|
||||
fieldSlot->data.buffer.offset = offset;
|
||||
fieldSlot->data.buffer.size = sizeof(double);
|
||||
#endif
|
||||
fieldSlot->type = FIELD_TYPE_FLOAT;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CursorWindow::putNull(unsigned int row, unsigned int col)
|
||||
{
|
||||
field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
|
||||
if (!fieldSlot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fieldSlot->type = FIELD_TYPE_NULL;
|
||||
fieldSlot->data.buffer.offset = 0;
|
||||
fieldSlot->data.buffer.size = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CursorWindow::getLong(unsigned int row, unsigned int col, int64_t * valueOut)
|
||||
{
|
||||
field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
|
||||
if (!fieldSlot || fieldSlot->type != FIELD_TYPE_INTEGER) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if WINDOW_STORAGE_INLINE_NUMERICS
|
||||
*valueOut = fieldSlot->data.l;
|
||||
#else
|
||||
*valueOut = copyOutLong(fieldSlot->data.buffer.offset);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CursorWindow::getDouble(unsigned int row, unsigned int col, double * valueOut)
|
||||
{
|
||||
field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
|
||||
if (!fieldSlot || fieldSlot->type != FIELD_TYPE_FLOAT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if WINDOW_STORAGE_INLINE_NUMERICS
|
||||
*valueOut = fieldSlot->data.d;
|
||||
#else
|
||||
*valueOut = copyOutDouble(fieldSlot->data.buffer.offset);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CursorWindow::getNull(unsigned int row, unsigned int col, bool * valueOut)
|
||||
{
|
||||
field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
|
||||
if (!fieldSlot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fieldSlot->type != FIELD_TYPE_NULL) {
|
||||
*valueOut = false;
|
||||
} else {
|
||||
*valueOut = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}; // namespace sqlcipher
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _ANDROID__DATABASE_WINDOW_H
|
||||
#define _ANDROID__DATABASE_WINDOW_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <jni.h>
|
||||
#include "log.h"
|
||||
#include <unordered_map>
|
||||
|
||||
#define ROW_SLOT_CHUNK_NUM_ROWS 128
|
||||
#define INITIAL_WINDOW_SIZE (1024 * 1024)
|
||||
#define GROW_WINDOW_SIZE_EXTRA INITIAL_WINDOW_SIZE
|
||||
#define WINDOW_ALLOCATION_UNBOUNDED 0
|
||||
|
||||
// Row slots are allocated in chunks of ROW_SLOT_CHUNK_NUM_ROWS,
|
||||
// with an offset after the rows that points to the next chunk
|
||||
#define ROW_SLOT_CHUNK_SIZE ((ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t)) + sizeof(uint32_t))
|
||||
|
||||
#if LOG_NDEBUG
|
||||
|
||||
#define IF_LOG_WINDOW() if (false)
|
||||
#define LOG_WINDOW(...)
|
||||
|
||||
#else
|
||||
|
||||
#define IF_LOG_WINDOW() IF_LOG(LOG_DEBUG, "CursorWindow")
|
||||
#define LOG_WINDOW(...) LOG(LOG_DEBUG, "CursorWindow", __VA_ARGS__)
|
||||
|
||||
#endif
|
||||
|
||||
// When defined to true strings are stored as UTF8, otherwise they're UTF16
|
||||
#define WINDOW_STORAGE_UTF8 0
|
||||
|
||||
// When defined to true numberic values are stored inline in the field_slot_t,
|
||||
// otherwise they're allocated in the window
|
||||
#define WINDOW_STORAGE_INLINE_NUMERICS 1
|
||||
|
||||
using std::make_pair;
|
||||
using std::tr1::unordered_map;
|
||||
|
||||
namespace sqlcipher {
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t numRows;
|
||||
uint32_t numColumns;
|
||||
} window_header_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t offset;
|
||||
} row_slot_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t type;
|
||||
union {
|
||||
double d;
|
||||
int64_t l;
|
||||
struct {
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
} buffer;
|
||||
} data;
|
||||
} __attribute__((packed)) field_slot_t;
|
||||
|
||||
#define FIELD_TYPE_INTEGER 1
|
||||
#define FIELD_TYPE_FLOAT 2
|
||||
#define FIELD_TYPE_STRING 3
|
||||
#define FIELD_TYPE_BLOB 4
|
||||
#define FIELD_TYPE_NULL 0
|
||||
|
||||
/**
|
||||
* This class stores a set of rows from a database in a buffer. The begining of the
|
||||
* window has first chunk of row_slot_ts, which are offsets to the row directory, followed by
|
||||
* an offset to the next chunk in a linked-list of additional chunk of row_slot_ts in case
|
||||
* the pre-allocated chunk isn't big enough to refer to all rows. Each row directory has a
|
||||
* field_slot_t per column, which has the size, offset, and type of the data for that field.
|
||||
* Note that the data types come from sqlite3.h.
|
||||
*/
|
||||
class CursorWindow
|
||||
{
|
||||
public:
|
||||
CursorWindow(size_t initialSize, size_t growthPaddingSize, size_t maxSize);
|
||||
CursorWindow(){}
|
||||
~CursorWindow();
|
||||
|
||||
bool initBuffer(bool localOnly);
|
||||
size_t size() {return mSize;}
|
||||
uint8_t * data() {return mData;}
|
||||
uint32_t getNumRows() {return mHeader->numRows;}
|
||||
uint32_t getNumColumns() {return mHeader->numColumns;}
|
||||
void freeLastRow() {
|
||||
if (mHeader->numRows > 0) {
|
||||
mHeader->numRows--;
|
||||
}
|
||||
}
|
||||
bool setNumColumns(uint32_t numColumns)
|
||||
{
|
||||
uint32_t cur = mHeader->numColumns;
|
||||
if (cur > 0 && cur != numColumns) {
|
||||
LOGE("Trying to go from %d columns to %d", cur, numColumns);
|
||||
return false;
|
||||
}
|
||||
mHeader->numColumns = numColumns;
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t freeSpace();
|
||||
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Allocate a row slot and its directory. The returned
|
||||
* pointer points to the begining of the row's directory
|
||||
* or NULL if there wasn't room. The directory is
|
||||
* initialied with NULL entries for each field.
|
||||
*/
|
||||
field_slot_t * allocRow();
|
||||
|
||||
/**
|
||||
* Allocate a portion of the window. Returns the offset
|
||||
* of the allocation, or 0 if there isn't enough space.
|
||||
* If aligned is true, the allocation gets 4 byte alignment.
|
||||
*/
|
||||
uint32_t alloc(size_t size, bool aligned = false);
|
||||
|
||||
uint32_t read_field_slot(int row, int column, field_slot_t * slot);
|
||||
|
||||
/**
|
||||
* Copy data into the window at the given offset.
|
||||
*/
|
||||
void copyIn(uint32_t offset, uint8_t const * data, size_t size);
|
||||
void copyIn(uint32_t offset, int64_t data);
|
||||
void copyIn(uint32_t offset, double data);
|
||||
|
||||
void copyOut(uint32_t offset, uint8_t * data, size_t size);
|
||||
int64_t copyOutLong(uint32_t offset);
|
||||
double copyOutDouble(uint32_t offset);
|
||||
|
||||
bool putLong(unsigned int row, unsigned int col, int64_t value);
|
||||
bool putDouble(unsigned int row, unsigned int col, double value);
|
||||
bool putNull(unsigned int row, unsigned int col);
|
||||
|
||||
bool getLong(unsigned int row, unsigned int col, int64_t * valueOut);
|
||||
bool getDouble(unsigned int row, unsigned int col, double * valueOut);
|
||||
bool getNull(unsigned int row, unsigned int col, bool * valueOut);
|
||||
|
||||
uint8_t * offsetToPtr(uint32_t offset) {return mData + offset;}
|
||||
|
||||
row_slot_t * allocRowSlot();
|
||||
|
||||
row_slot_t * getRowSlot(int row);
|
||||
|
||||
/**
|
||||
* return NULL if Failed to find rowSlot or
|
||||
* Invalid rowSlot
|
||||
*/
|
||||
field_slot_t * getFieldSlotWithCheck(int row, int column);
|
||||
field_slot_t * getFieldSlot(int row, int column)
|
||||
{
|
||||
int fieldDirOffset = getRowSlot(row)->offset;
|
||||
return ((field_slot_t *)offsetToPtr(fieldDirOffset)) + column;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t * mData;
|
||||
size_t mSize;
|
||||
size_t mInitialSize;
|
||||
size_t mGrowthPaddingSize;
|
||||
size_t mMaxSize;
|
||||
window_header_t * mHeader;
|
||||
/**
|
||||
* Offset of the lowest unused data byte in the array.
|
||||
*/
|
||||
uint32_t mFreeOffset;
|
||||
unordered_map<int, uint32_t> mChunkNumToNextChunkOffset;
|
||||
int mLastChunkPtrOffset;
|
||||
};
|
||||
|
||||
}; // namespace sqlcipher
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
#ifndef NELEM
|
||||
# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
|
||||
#endif
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#include <cstddef>
|
||||
#include "jni_exception.h"
|
||||
|
||||
void jniThrowException(JNIEnv* env, const char* exceptionClass, const char* sqlite3Message) {
|
||||
jclass exClass;
|
||||
exClass = env->FindClass(exceptionClass);
|
||||
env->ThrowNew(exClass, sqlite3Message);
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
#include <jni.h>
|
||||
|
||||
#ifndef _JNI_EXCEPTION_H
|
||||
#define _JNI_EXCEPTION_H
|
||||
void jniThrowException(JNIEnv* env, const char* exceptionClass, const char* sqlite3Message);
|
||||
#endif
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
#include <android/log.h>
|
||||
|
||||
#ifdef LOG_NDEBUG
|
||||
#define LOGI(...)
|
||||
#define LOGE(...)
|
||||
#define LOGV(...)
|
||||
#define LOGD(...)
|
||||
#else
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
|
||||
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE,LOG_TAG,__VA_ARGS__)
|
||||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifndef LOG
|
||||
#define LOG(priority, tag, ...) \
|
||||
LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifndef LOG_PRI
|
||||
#define LOG_PRI(priority, tag, ...) \
|
||||
__android_log_print(priority, tag, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifndef LOG_ASSERT
|
||||
#define LOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ## __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifndef LOG_FATAL_IF
|
||||
#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ## __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#ifndef LOG_ALWAYS_FATAL_IF
|
||||
#define LOG_ALWAYS_FATAL_IF(cond, ...) \
|
||||
( (CONDITION(cond)) \
|
||||
? ((void)android_printAssert(#cond, LOG_TAG, ## __VA_ARGS__)) \
|
||||
: (void)0 )
|
||||
#endif
|
||||
|
||||
#ifndef CONDITION
|
||||
#define CONDITION(cond) (__builtin_expect((cond)!=0, 0))
|
||||
#endif
|
||||
|
||||
#define android_printAssert(a, b, ...) printf("%s: ", __VA_ARGS__)
|
||||
|
|
@ -0,0 +1,680 @@
|
|||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "CursorWindow"
|
||||
|
||||
#include <jni.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "CursorWindow.h"
|
||||
#include "jni_elements.h"
|
||||
#include "jni_exception.h"
|
||||
#include "sqlite3_exception.h"
|
||||
|
||||
#include <wchar.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
namespace sqlcipher {
|
||||
|
||||
static jfieldID gWindowField;
|
||||
static jfieldID gBufferField;
|
||||
static jfieldID gSizeCopiedField;
|
||||
|
||||
#define GET_WINDOW(env, object) ((CursorWindow *)env->GetLongField(object, gWindowField))
|
||||
#define SET_WINDOW(env, object, window) (env->SetLongField(object, gWindowField,(intptr_t)window))
|
||||
#define SET_BUFFER(env, object, buf) (env->SetObjectField(object, gBufferField, buf))
|
||||
#define SET_SIZE_COPIED(env, object, size) (env->SetIntField(object, gSizeCopiedField, size))
|
||||
|
||||
CursorWindow * get_window_from_object(JNIEnv * env, jobject javaWindow)
|
||||
{
|
||||
return GET_WINDOW(env, javaWindow);
|
||||
}
|
||||
|
||||
static void native_init_empty(JNIEnv * env, jobject object,
|
||||
jboolean localOnly, jlong initialSize,
|
||||
jlong growthPaddingSize, jlong maxSize)
|
||||
{
|
||||
uint8_t * data;
|
||||
size_t size;
|
||||
CursorWindow * window;
|
||||
|
||||
window = new CursorWindow(initialSize, growthPaddingSize, maxSize);
|
||||
if (!window) {
|
||||
jniThrowException(env, "java/lang/RuntimeException", "No memory for native window object");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!window->initBuffer(localOnly)) {
|
||||
jniThrowException(env, "java/lang/IllegalStateException", "Couldn't init cursor window");
|
||||
delete window;
|
||||
return;
|
||||
}
|
||||
LOG_WINDOW("native_init_empty: window = %p", window);
|
||||
SET_WINDOW(env, object, window);
|
||||
}
|
||||
|
||||
static void native_clear(JNIEnv * env, jobject object)
|
||||
{
|
||||
CursorWindow * window = GET_WINDOW(env, object);
|
||||
LOG_WINDOW("Clearing window %p", window);
|
||||
if (window == NULL) {
|
||||
jniThrowException(env, "java/lang/IllegalStateException", "clear() called after close()");
|
||||
return;
|
||||
}
|
||||
window->clear();
|
||||
}
|
||||
|
||||
static void native_close(JNIEnv * env, jobject object)
|
||||
{
|
||||
CursorWindow * window = GET_WINDOW(env, object);
|
||||
if (window) {
|
||||
LOG_WINDOW("Closing window %p", window);
|
||||
delete window;
|
||||
SET_WINDOW(env, object, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void throwExceptionWithRowCol(JNIEnv * env, jint row, jint column)
|
||||
{
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf), "get field slot from row %d col %d failed", row, column);
|
||||
jniThrowException(env, "net/sqlcipher/InvalidRowColumnException", buf);
|
||||
}
|
||||
|
||||
static void throwUnknowTypeException(JNIEnv * env, jint type)
|
||||
{
|
||||
char buf[80];
|
||||
snprintf(buf, sizeof(buf), "UNKNOWN type %d", type);
|
||||
jniThrowException(env, "net/sqlcipher/UnknownTypeException", buf);
|
||||
}
|
||||
|
||||
static jlong getLong_native(JNIEnv * env, jobject object, jint row, jint column)
|
||||
{
|
||||
int32_t err;
|
||||
CursorWindow * window = GET_WINDOW(env, object);
|
||||
LOG_WINDOW("Getting long for %d,%d from %p", row, column, window);
|
||||
|
||||
field_slot_t field;
|
||||
err = window->read_field_slot(row, column, &field);
|
||||
if (err != 0) {
|
||||
throwExceptionWithRowCol(env, row, column);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t type = field.type;
|
||||
if (type == FIELD_TYPE_INTEGER) {
|
||||
int64_t value;
|
||||
if (window->getLong(row, column, &value)) {
|
||||
return value;
|
||||
}
|
||||
return 0;
|
||||
} else if (type == FIELD_TYPE_STRING) {
|
||||
uint32_t size = field.data.buffer.size;
|
||||
if (size > 0) {
|
||||
long long int result;
|
||||
jstring data = env->NewString((const jchar*)window->offsetToPtr(field.data.buffer.offset), (jsize)size / sizeof(jchar));
|
||||
const char* utf8data = env->GetStringUTFChars(data, NULL);
|
||||
result = strtoll(utf8data, NULL, 0);
|
||||
if(utf8data) env->ReleaseStringUTFChars(data, utf8data);
|
||||
if(data) env->DeleteLocalRef(data);
|
||||
return result;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else if (type == FIELD_TYPE_FLOAT) {
|
||||
double value;
|
||||
if (window->getDouble(row, column, &value)) {
|
||||
return value;
|
||||
}
|
||||
return 0;
|
||||
} else if (type == FIELD_TYPE_NULL) {
|
||||
return 0;
|
||||
} else if (type == FIELD_TYPE_BLOB) {
|
||||
throw_sqlite3_exception(env, "Unable to convert BLOB to long");
|
||||
return 0;
|
||||
} else {
|
||||
throwUnknowTypeException(env, type);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static jbyteArray getBlob_native(JNIEnv* env, jobject object, jint row, jint column)
|
||||
{
|
||||
int32_t err;
|
||||
CursorWindow * window = GET_WINDOW(env, object);
|
||||
LOG_WINDOW("Getting blob for %d,%d from %p", row, column, window);
|
||||
|
||||
field_slot_t field;
|
||||
err = window->read_field_slot(row, column, &field);
|
||||
if (err != 0) {
|
||||
throwExceptionWithRowCol(env, row, column);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t type = field.type;
|
||||
if (type == FIELD_TYPE_BLOB || type == FIELD_TYPE_STRING) {
|
||||
jbyteArray byteArray = env->NewByteArray(field.data.buffer.size);
|
||||
if(byteArray == NULL) return NULL;
|
||||
env->SetByteArrayRegion(byteArray, 0, field.data.buffer.size,
|
||||
(const jbyte*)window->offsetToPtr(field.data.buffer.offset));
|
||||
return byteArray;
|
||||
} else if (type == FIELD_TYPE_INTEGER) {
|
||||
throw_sqlite3_exception(env, "INTEGER data in getBlob_native ");
|
||||
} else if (type == FIELD_TYPE_FLOAT) {
|
||||
throw_sqlite3_exception(env, "FLOAT data in getBlob_native ");
|
||||
} else if (type == FIELD_TYPE_NULL) {
|
||||
// do nothing
|
||||
} else {
|
||||
throwUnknowTypeException(env, type);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static jboolean isBlob_native(JNIEnv* env, jobject object, jint row, jint column)
|
||||
{
|
||||
int32_t err;
|
||||
CursorWindow * window = GET_WINDOW(env, object);
|
||||
LOG_WINDOW("Checking if column is a blob or null for %d,%d from %p", row, column, window);
|
||||
field_slot_t field;
|
||||
err = window->read_field_slot(row, column, &field);
|
||||
if (err != 0) {
|
||||
throwExceptionWithRowCol(env, row, column);
|
||||
return false;
|
||||
}
|
||||
return field.type == FIELD_TYPE_BLOB || field.type == FIELD_TYPE_NULL;
|
||||
}
|
||||
|
||||
static jboolean isString_native(JNIEnv* env, jobject object, jint row, jint column)
|
||||
{
|
||||
int32_t err;
|
||||
CursorWindow * window = GET_WINDOW(env, object);
|
||||
LOG_WINDOW("Checking if column is a string or null for %d,%d from %p", row, column, window);
|
||||
field_slot_t field;
|
||||
err = window->read_field_slot(row, column, &field);
|
||||
if (err != 0) {
|
||||
throwExceptionWithRowCol(env, row, column);
|
||||
return false;
|
||||
}
|
||||
return field.type == FIELD_TYPE_STRING || field.type == FIELD_TYPE_NULL;
|
||||
}
|
||||
|
||||
static jboolean isInteger_native(JNIEnv* env, jobject object, jint row, jint column)
|
||||
{
|
||||
int32_t err;
|
||||
CursorWindow * window = GET_WINDOW(env, object);
|
||||
LOG_WINDOW("Checking if column is an integer for %d,%d from %p", row, column, window);
|
||||
field_slot_t field;
|
||||
err = window->read_field_slot(row, column, &field);
|
||||
if (err != 0) {
|
||||
throwExceptionWithRowCol(env, row, column);
|
||||
return false;
|
||||
}
|
||||
return field.type == FIELD_TYPE_INTEGER;
|
||||
}
|
||||
|
||||
static jint getType_native(JNIEnv* env, jobject object, jint row, jint column)
|
||||
{
|
||||
int32_t err;
|
||||
CursorWindow * window = GET_WINDOW(env, object);
|
||||
LOG_WINDOW("Getting type for %d,%d from %p", row, column, window);
|
||||
field_slot_t field;
|
||||
err = window->read_field_slot(row, column, &field);
|
||||
if (err != 0) {
|
||||
throwExceptionWithRowCol(env, row, column);
|
||||
return false;
|
||||
}
|
||||
return field.type;
|
||||
}
|
||||
|
||||
static jboolean isFloat_native(JNIEnv* env, jobject object, jint row, jint column)
|
||||
{
|
||||
int32_t err;
|
||||
CursorWindow * window = GET_WINDOW(env, object);
|
||||
LOG_WINDOW("Checking if column is a float for %d,%d from %p", row, column, window);
|
||||
field_slot_t field;
|
||||
err = window->read_field_slot(row, column, &field);
|
||||
if (err != 0) {
|
||||
throwExceptionWithRowCol(env, row, column);
|
||||
return false;
|
||||
}
|
||||
return field.type == FIELD_TYPE_FLOAT;
|
||||
}
|
||||
|
||||
static jstring getString_native(JNIEnv* env, jobject object, jint row, jint column)
|
||||
{
|
||||
int i;
|
||||
int32_t err;
|
||||
CursorWindow * window = GET_WINDOW(env, object);
|
||||
LOG_WINDOW("Getting string for %d,%d from %p", row, column, window);
|
||||
field_slot_t field;
|
||||
err = window->read_field_slot(row, column, &field);
|
||||
if (err != 0) {
|
||||
throwExceptionWithRowCol(env, row, column);
|
||||
return NULL;
|
||||
}
|
||||
uint8_t type = field.type;
|
||||
jint size = (jint)field.data.buffer.size;
|
||||
if (type == FIELD_TYPE_NULL) {
|
||||
return NULL;
|
||||
} else if (type == FIELD_TYPE_BLOB) {
|
||||
throw_sqlite3_exception(env, "Unable to convert BLOB to string");
|
||||
return NULL;
|
||||
} else if (type == FIELD_TYPE_STRING) {
|
||||
return env->NewString((const jchar*)window->offsetToPtr(field.data.buffer.offset), (jsize)size / sizeof(jchar));
|
||||
} else if (type == FIELD_TYPE_INTEGER) {
|
||||
int64_t value;
|
||||
if (window->getLong(row, column, &value)) {
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "%"PRId64"", value);
|
||||
return env->NewStringUTF((const char*)buf);
|
||||
}
|
||||
return NULL;
|
||||
} else if (type == FIELD_TYPE_FLOAT) {
|
||||
double value;
|
||||
if (window->getDouble(row, column, &value)) {
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "%g", value);
|
||||
return env->NewStringUTF(buf);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this only to convert characters that are known to be within the
|
||||
* 0-127 range for direct conversion to UTF-16
|
||||
*/
|
||||
static jint charToJchar(const char* src, jchar* dst, jint bufferSize)
|
||||
{
|
||||
int32_t len = strlen(src);
|
||||
if (bufferSize < len) {
|
||||
len = bufferSize;
|
||||
}
|
||||
for (int i = 0; i < len; i++) {
|
||||
*dst++ = (*src++ & 0x7F);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static jcharArray copyStringToBuffer_native(JNIEnv* env, jobject object, jint row,
|
||||
jint column, jint bufferSize, jobject buf)
|
||||
{
|
||||
int32_t err;
|
||||
CursorWindow * window = GET_WINDOW(env, object);
|
||||
LOG_WINDOW("Copying string for %d,%d from %p", row, column, window);
|
||||
|
||||
field_slot_t field;
|
||||
err = window->read_field_slot(row, column, &field);
|
||||
if (err != 0) {
|
||||
jniThrowException(env, "java/lang/IllegalStateException", "Unable to get field slot");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jcharArray buffer = (jcharArray)env->GetObjectField(buf, gBufferField);
|
||||
if (buffer == NULL) {
|
||||
jniThrowException(env, "java/lang/IllegalStateException", "buf should not be null");
|
||||
return NULL;
|
||||
}
|
||||
jchar* dst = env->GetCharArrayElements(buffer, NULL);
|
||||
uint8_t type = field.type;
|
||||
uint32_t sizeCopied = 0;
|
||||
jcharArray newArray = NULL;
|
||||
if (type == FIELD_TYPE_STRING) {
|
||||
uint32_t size = field.data.buffer.size;
|
||||
if (size > 0) {
|
||||
jsize length = (jsize)size/sizeof(jchar);
|
||||
int32_t strSize = (jsize)size/sizeof(jchar);
|
||||
jstring content = env->NewString((const jchar *)window->offsetToPtr(field.data.buffer.offset), length);
|
||||
const jchar *elements = env->GetStringChars(content, JNI_FALSE);
|
||||
if (strSize > bufferSize || dst == NULL) {
|
||||
newArray = env->NewCharArray(length);
|
||||
env->SetCharArrayRegion(newArray, 0, length, elements);
|
||||
if(elements) env->ReleaseStringChars(content, elements);
|
||||
if(content) env->DeleteLocalRef(content);
|
||||
} else {
|
||||
memcpy(dst, elements, strSize * 2);
|
||||
}
|
||||
sizeCopied = strSize;
|
||||
}
|
||||
} else if (type == FIELD_TYPE_INTEGER) {
|
||||
int64_t value;
|
||||
if (window->getLong(row, column, &value)) {
|
||||
int len;
|
||||
char buf[32];
|
||||
len = snprintf(buf, sizeof(buf), "%"PRId64"", value);
|
||||
jint bufferLength = env->GetArrayLength(buffer);
|
||||
if(len > bufferLength || dst == NULL){
|
||||
jstring content = env->NewStringUTF(buf);
|
||||
const jchar *elements = env->GetStringChars(content, JNI_FALSE);
|
||||
newArray = env->NewCharArray(len);
|
||||
env->SetCharArrayRegion(newArray, 0, len, elements);
|
||||
sizeCopied = len;
|
||||
if(elements) env->ReleaseStringChars(content, elements);
|
||||
if(content) env->DeleteLocalRef(content);
|
||||
} else {
|
||||
memcpy(dst, buf, len);
|
||||
sizeCopied = charToJchar(buf, dst, bufferSize);
|
||||
}
|
||||
}
|
||||
} else if (type == FIELD_TYPE_FLOAT) {
|
||||
double value;
|
||||
if (window->getDouble(row, column, &value)) {
|
||||
int len;
|
||||
char buf[32];
|
||||
len = snprintf(buf, sizeof(buf), "%g", value);
|
||||
jint bufferLength = env->GetArrayLength(buffer);
|
||||
if(len > bufferLength || dst == NULL){
|
||||
jstring content = env->NewStringUTF(buf);
|
||||
const jchar *elements = env->GetStringChars(content, JNI_FALSE);
|
||||
newArray = env->NewCharArray(len);
|
||||
env->SetCharArrayRegion(newArray, 0, len, elements);
|
||||
sizeCopied = len;
|
||||
if(elements) env->ReleaseStringChars(content, elements);
|
||||
if(content) env->DeleteLocalRef(content);
|
||||
} else {
|
||||
memcpy(dst, buf, len);
|
||||
sizeCopied = charToJchar(buf, dst, bufferSize);
|
||||
}
|
||||
}
|
||||
} else if (type == FIELD_TYPE_NULL) {
|
||||
} else if (type == FIELD_TYPE_BLOB) {
|
||||
throw_sqlite3_exception(env, "Unable to convert BLOB to string");
|
||||
} else {
|
||||
LOGE("Unknown field type %d", type);
|
||||
throw_sqlite3_exception(env, "UNKNOWN type in copyStringToBuffer_native()");
|
||||
}
|
||||
SET_SIZE_COPIED(env, buf, sizeCopied);
|
||||
env->ReleaseCharArrayElements(buffer, dst, JNI_OK);
|
||||
return newArray;
|
||||
}
|
||||
|
||||
static jdouble getDouble_native(JNIEnv* env, jobject object, jint row, jint column)
|
||||
{
|
||||
int32_t err;
|
||||
CursorWindow * window = GET_WINDOW(env, object);
|
||||
LOG_WINDOW("Getting double for %d,%d from %p", row, column, window);
|
||||
|
||||
field_slot_t field;
|
||||
err = window->read_field_slot(row, column, &field);
|
||||
if (err != 0) {
|
||||
throwExceptionWithRowCol(env, row, column);
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
uint8_t type = field.type;
|
||||
if (type == FIELD_TYPE_FLOAT) {
|
||||
double value;
|
||||
if (window->getDouble(row, column, &value)) {
|
||||
return value;
|
||||
}
|
||||
return 0.0;
|
||||
} else if (type == FIELD_TYPE_STRING) {
|
||||
uint32_t size = field.data.buffer.size;
|
||||
if (size > 0) {
|
||||
double result;
|
||||
jstring data = env->NewString((const jchar*)window->offsetToPtr(field.data.buffer.offset), (jsize)size / sizeof(jchar));
|
||||
const char* utf8data = env->GetStringUTFChars(data, NULL);
|
||||
result = strtod(utf8data, NULL);
|
||||
if(utf8data) env->ReleaseStringUTFChars(data, utf8data);
|
||||
if(data) env->DeleteLocalRef(data);
|
||||
return result;
|
||||
} else {
|
||||
return 0.0;
|
||||
}
|
||||
} else if (type == FIELD_TYPE_INTEGER) {
|
||||
int64_t value;
|
||||
if (window->getLong(row, column, &value)) {
|
||||
return (double) value;
|
||||
}
|
||||
return 0.0;
|
||||
} else if (type == FIELD_TYPE_NULL) {
|
||||
return 0.0;
|
||||
} else if (type == FIELD_TYPE_BLOB) {
|
||||
throw_sqlite3_exception(env, "Unable to convert BLOB to double");
|
||||
return 0.0;
|
||||
} else {
|
||||
throwUnknowTypeException(env, type);
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
static jboolean isNull_native(JNIEnv* env, jobject object, jint row, jint column)
|
||||
{
|
||||
CursorWindow * window = GET_WINDOW(env, object);
|
||||
LOG_WINDOW("Checking for NULL at %d,%d from %p", row, column, window);
|
||||
bool isNull;
|
||||
if (window->getNull(row, column, &isNull)) {
|
||||
return isNull;
|
||||
}
|
||||
//TODO throw execption?
|
||||
return true;
|
||||
}
|
||||
|
||||
static jint getNumRows(JNIEnv * env, jobject object)
|
||||
{
|
||||
CursorWindow * window = GET_WINDOW(env, object);
|
||||
return window->getNumRows();
|
||||
}
|
||||
|
||||
static jboolean setNumColumns(JNIEnv * env, jobject object, jint columnNum)
|
||||
{
|
||||
CursorWindow * window = GET_WINDOW(env, object);
|
||||
return window->setNumColumns(columnNum);
|
||||
}
|
||||
|
||||
static jboolean allocRow(JNIEnv * env, jobject object)
|
||||
{
|
||||
CursorWindow * window = GET_WINDOW(env, object);
|
||||
return window->allocRow() != NULL;
|
||||
}
|
||||
|
||||
static jboolean putBlob_native(JNIEnv * env, jobject object, jbyteArray value, jint row, jint col)
|
||||
{
|
||||
CursorWindow * window = GET_WINDOW(env, object);
|
||||
if (!value) {
|
||||
LOG_WINDOW("How did a null value send to here");
|
||||
return false;
|
||||
}
|
||||
field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, col);
|
||||
if (fieldSlot == NULL) {
|
||||
LOG_WINDOW(" getFieldSlotWithCheck error ");
|
||||
return false;
|
||||
}
|
||||
|
||||
jint len = env->GetArrayLength(value);
|
||||
int offset = window->alloc(len);
|
||||
if (!offset) {
|
||||
LOG_WINDOW("Failed allocating %u bytes", len);
|
||||
return false;
|
||||
}
|
||||
jbyte * bytes = env->GetByteArrayElements(value, NULL);
|
||||
window->copyIn(offset, (uint8_t const *)bytes, len);
|
||||
|
||||
// This must be updated after the call to alloc(), since that
|
||||
// may move the field around in the window
|
||||
fieldSlot->type = FIELD_TYPE_BLOB;
|
||||
fieldSlot->data.buffer.offset = offset;
|
||||
fieldSlot->data.buffer.size = len;
|
||||
env->ReleaseByteArrayElements(value, bytes, JNI_ABORT);
|
||||
LOG_WINDOW("%d,%d is BLOB with %u bytes @ %d", row, col, len, offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
static jboolean putString_native(JNIEnv * env, jobject object, jstring value, jint row, jint col)
|
||||
{
|
||||
CursorWindow * window = GET_WINDOW(env, object);
|
||||
if (!value) {
|
||||
LOG_WINDOW("How did a null value send to here");
|
||||
return false;
|
||||
}
|
||||
field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, col);
|
||||
if (fieldSlot == NULL) {
|
||||
LOG_WINDOW(" getFieldSlotWithCheck error ");
|
||||
return false;
|
||||
}
|
||||
|
||||
#if WINDOW_STORAGE_UTF8
|
||||
int len = env->GetStringUTFLength(value) + 1;
|
||||
char const * valStr = env->GetStringUTFChars(value, NULL);
|
||||
#else
|
||||
int len = env->GetStringLength(value);
|
||||
// GetStringLength return number of chars and one char takes 2 bytes
|
||||
len *= 2;
|
||||
const jchar* valStr = env->GetStringChars(value, NULL);
|
||||
#endif
|
||||
if (!valStr) {
|
||||
LOG_WINDOW("value can't be transfer to UTFChars");
|
||||
return false;
|
||||
}
|
||||
|
||||
int offset = window->alloc(len);
|
||||
if (!offset) {
|
||||
LOG_WINDOW("Failed allocating %u bytes", len);
|
||||
#if WINDOW_STORAGE_UTF8
|
||||
env->ReleaseStringUTFChars(value, valStr);
|
||||
#else
|
||||
env->ReleaseStringChars(value, valStr);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
window->copyIn(offset, (uint8_t const *)valStr, len);
|
||||
|
||||
// This must be updated after the call to alloc(), since that
|
||||
// may move the field around in the window
|
||||
fieldSlot->type = FIELD_TYPE_STRING;
|
||||
fieldSlot->data.buffer.offset = offset;
|
||||
fieldSlot->data.buffer.size = len;
|
||||
|
||||
LOG_WINDOW("%d,%d is TEXT with %u bytes @ %d", row, col, len, offset);
|
||||
#if WINDOW_STORAGE_UTF8
|
||||
env->ReleaseStringUTFChars(value, valStr);
|
||||
#else
|
||||
env->ReleaseStringChars(value, valStr);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static jboolean putLong_native(JNIEnv * env, jobject object, jlong value, jint row, jint col)
|
||||
{
|
||||
CursorWindow * window = GET_WINDOW(env, object);
|
||||
if (!window->putLong(row, col, value)) {
|
||||
LOG_WINDOW(" getFieldSlotWithCheck error ");
|
||||
return false;
|
||||
}
|
||||
LOG_WINDOW("%d,%d is INTEGER 0x%016llx", row, col, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
static jboolean putDouble_native(JNIEnv * env, jobject object, jdouble value, jint row, jint col)
|
||||
{
|
||||
CursorWindow * window = GET_WINDOW(env, object);
|
||||
if (!window->putDouble(row, col, value)) {
|
||||
LOG_WINDOW(" getFieldSlotWithCheck error ");
|
||||
return false;
|
||||
}
|
||||
LOG_WINDOW("%d,%d is FLOAT %lf", row, col, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
static jboolean putNull_native(JNIEnv * env, jobject object, jint row, jint col)
|
||||
{
|
||||
CursorWindow * window = GET_WINDOW(env, object);
|
||||
if (!window->putNull(row, col)) {
|
||||
LOG_WINDOW(" getFieldSlotWithCheck error ");
|
||||
return false;
|
||||
}
|
||||
LOG_WINDOW("%d,%d is NULL", row, col);
|
||||
return true;
|
||||
}
|
||||
|
||||
// free the last row
|
||||
static void freeLastRow(JNIEnv * env, jobject object) {
|
||||
CursorWindow * window = GET_WINDOW(env, object);
|
||||
window->freeLastRow();
|
||||
}
|
||||
|
||||
static JNINativeMethod sMethods[] =
|
||||
{
|
||||
/* name, signature, funcPtr */
|
||||
{"native_init", "(ZJJJ)V", (void *)native_init_empty},
|
||||
// {"native_init", "(Landroid/os/IBinder;)V", (void *)native_init_memory},
|
||||
// {"native_getBinder", "()Landroid/os/IBinder;", (void *)native_getBinder},
|
||||
{"native_clear", "()V", (void *)native_clear},
|
||||
{"close_native", "()V", (void *)native_close},
|
||||
{"getLong_native", "(II)J", (void *)getLong_native},
|
||||
{"getBlob_native", "(II)[B", (void *)getBlob_native},
|
||||
{"isBlob_native", "(II)Z", (void *)isBlob_native},
|
||||
{"getString_native", "(II)Ljava/lang/String;", (void *)getString_native},
|
||||
//{"getString_native", "(II)[B", (void *)getString_native},
|
||||
{"copyStringToBuffer_native", "(IIILandroid/database/CharArrayBuffer;)[C", (void *)copyStringToBuffer_native},
|
||||
{"getDouble_native", "(II)D", (void *)getDouble_native},
|
||||
{"isNull_native", "(II)Z", (void *)isNull_native},
|
||||
{"getNumRows_native", "()I", (void *)getNumRows},
|
||||
{"setNumColumns_native", "(I)Z", (void *)setNumColumns},
|
||||
{"allocRow_native", "()Z", (void *)allocRow},
|
||||
{"putBlob_native", "([BII)Z", (void *)putBlob_native},
|
||||
{"putString_native", "(Ljava/lang/String;II)Z", (void *)putString_native},
|
||||
{"putLong_native", "(JII)Z", (void *)putLong_native},
|
||||
{"putDouble_native", "(DII)Z", (void *)putDouble_native},
|
||||
{"freeLastRow_native", "()V", (void *)freeLastRow},
|
||||
{"putNull_native", "(II)Z", (void *)putNull_native},
|
||||
{"isString_native", "(II)Z", (void *)isString_native},
|
||||
{"isFloat_native", "(II)Z", (void *)isFloat_native},
|
||||
{"isInteger_native", "(II)Z", (void *)isInteger_native},
|
||||
{"getType_native", "(II)I", (void *)getType_native},
|
||||
};
|
||||
|
||||
int register_android_database_CursorWindow(JNIEnv * env)
|
||||
{
|
||||
jclass clazz;
|
||||
clazz = env->FindClass("net/sqlcipher/CursorWindow");
|
||||
if (clazz == NULL) {
|
||||
LOGE("Can't find net/sqlcipher/CursorWindow");
|
||||
return -1;
|
||||
}
|
||||
gWindowField = env->GetFieldID(clazz, "nWindow", "J");
|
||||
if (gWindowField == NULL) {
|
||||
LOGE("Error locating fields");
|
||||
return -1;
|
||||
}
|
||||
clazz = env->FindClass("android/database/CharArrayBuffer");
|
||||
if (clazz == NULL) {
|
||||
LOGE("Can't find android/database/CharArrayBuffer");
|
||||
return -1;
|
||||
}
|
||||
gBufferField = env->GetFieldID(clazz, "data", "[C");
|
||||
if (gBufferField == NULL) {
|
||||
LOGE("Error locating fields data in CharArrayBuffer");
|
||||
return -1;
|
||||
}
|
||||
gSizeCopiedField = env->GetFieldID(clazz, "sizeCopied", "I");
|
||||
if (gSizeCopiedField == NULL) {
|
||||
LOGE("Error locating fields sizeCopied in CharArrayBuffer");
|
||||
return -1;
|
||||
}
|
||||
clazz = env->FindClass("net/sqlcipher/CursorWindow");
|
||||
return env->RegisterNatives(clazz, sMethods, NELEM(sMethods));
|
||||
}
|
||||
} // namespace sqlcipher
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright (C) 2006-2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "Cursor"
|
||||
|
||||
#include <jni.h>
|
||||
// #include <JNIHelp.h>
|
||||
// #include <android_runtime/AndroidRuntime.h>
|
||||
// #include <utils/Log.h>
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "log.h"
|
||||
#include "jni_elements.h"
|
||||
#include "jni_exception.h"
|
||||
#include "sqlite3_exception.h"
|
||||
|
||||
namespace sqlcipher {
|
||||
|
||||
static jfieldID gHandleField;
|
||||
static jfieldID gStatementField;
|
||||
|
||||
|
||||
#define GET_STATEMENT(env, object) \
|
||||
(sqlite3_stmt *)env->GetLongField(object, gStatementField)
|
||||
#define GET_HANDLE(env, object) \
|
||||
(sqlite3 *)env->GetLongField(object, gHandleField)
|
||||
|
||||
|
||||
sqlite3_stmt * compile(JNIEnv* env, jobject object,
|
||||
sqlite3 * handle, jstring sqlString)
|
||||
{
|
||||
int err;
|
||||
jchar const * sql;
|
||||
jsize sqlLen;
|
||||
sqlite3_stmt * statement = GET_STATEMENT(env, object);
|
||||
|
||||
// Make sure not to leak the statement if it already exists
|
||||
if (statement != NULL) {
|
||||
sqlite3_finalize(statement);
|
||||
env->SetLongField(object, gStatementField, 0);
|
||||
}
|
||||
|
||||
// Compile the SQL
|
||||
sql = env->GetStringChars(sqlString, NULL);
|
||||
sqlLen = env->GetStringLength(sqlString);
|
||||
err = sqlite3_prepare16_v2(handle, sql, sqlLen * 2, &statement, NULL);
|
||||
env->ReleaseStringChars(sqlString, sql);
|
||||
|
||||
if (err == SQLITE_OK) {
|
||||
// Store the statement in the Java object for future calls
|
||||
LOGV("Prepared statement %p on %p", statement, handle);
|
||||
env->SetLongField(object, gStatementField, (intptr_t)statement);
|
||||
return statement;
|
||||
} else {
|
||||
// Error messages like 'near ")": syntax error' are not
|
||||
// always helpful enough, so construct an error string that
|
||||
// includes the query itself.
|
||||
const char *query = env->GetStringUTFChars(sqlString, NULL);
|
||||
char *message = (char*) malloc(strlen(query) + 50);
|
||||
if (message) {
|
||||
strcpy(message, ", while compiling: "); // less than 50 chars
|
||||
strcat(message, query);
|
||||
}
|
||||
env->ReleaseStringUTFChars(sqlString, query);
|
||||
throw_sqlite3_exception(env, handle, message);
|
||||
free(message);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void native_compile(JNIEnv* env, jobject object, jstring sqlString)
|
||||
{
|
||||
compile(env, object, GET_HANDLE(env, object), sqlString);
|
||||
}
|
||||
|
||||
static void native_finalize(JNIEnv* env, jobject object)
|
||||
{
|
||||
int err;
|
||||
sqlite3_stmt * statement = GET_STATEMENT(env, object);
|
||||
|
||||
if (statement != NULL) {
|
||||
sqlite3_finalize(statement);
|
||||
env->SetLongField(object, gStatementField, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static JNINativeMethod sMethods[] =
|
||||
{
|
||||
/* name, signature, funcPtr */
|
||||
{"native_compile", "(Ljava/lang/String;)V", (void *)native_compile},
|
||||
{"native_finalize", "()V", (void *)native_finalize},
|
||||
};
|
||||
|
||||
int register_android_database_SQLiteCompiledSql(JNIEnv * env)
|
||||
{
|
||||
jclass clazz;
|
||||
|
||||
clazz = env->FindClass("net/sqlcipher/database/SQLiteCompiledSql");
|
||||
if (clazz == NULL) {
|
||||
LOGE("Can't find net/sqlcipher/database/SQLiteCompiledSql");
|
||||
return -1;
|
||||
}
|
||||
|
||||
gHandleField = env->GetFieldID(clazz, "nHandle", "J");
|
||||
gStatementField = env->GetFieldID(clazz, "nStatement", "J");
|
||||
|
||||
if (gHandleField == NULL || gStatementField == NULL) {
|
||||
LOGE("Error locating fields");
|
||||
return -1;
|
||||
}
|
||||
return env->RegisterNatives(clazz, sMethods, NELEM(sMethods));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace sqlcipher
|
||||
|
|
@ -0,0 +1,678 @@
|
|||
/*
|
||||
* Copyright (C) 2006-2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "Database"
|
||||
|
||||
#include <jni.h>
|
||||
#include <sqlite3.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "jni_elements.h"
|
||||
#include "jni_exception.h"
|
||||
#include "sqlite3_exception.h"
|
||||
#include "sqlcipher_loading.h"
|
||||
|
||||
#define UTF16_STORAGE 0
|
||||
#define INVALID_VERSION -1
|
||||
#define SQLITE_SOFT_HEAP_LIMIT (4 * 1024 * 1024)
|
||||
#define ANDROID_TABLE "android_metadata"
|
||||
/* uncomment the next line to force-enable logging of all statements */
|
||||
// #define DB_LOG_STATEMENTS
|
||||
|
||||
namespace sqlcipher {
|
||||
|
||||
|
||||
enum {
|
||||
OPEN_READWRITE = 0x00000000,
|
||||
OPEN_READONLY = 0x00000001,
|
||||
OPEN_READ_MASK = 0x00000001,
|
||||
NO_LOCALIZED_COLLATORS = 0x00000010,
|
||||
CREATE_IF_NECESSARY = 0x10000000
|
||||
};
|
||||
|
||||
static jfieldID offset_db_handle;
|
||||
|
||||
static char *createStr(const char *path) {
|
||||
int len = strlen(path);
|
||||
char *str = (char *)malloc(len + 1);
|
||||
strncpy(str, path, len);
|
||||
str[len] = 0;
|
||||
return str;
|
||||
}
|
||||
|
||||
static void sqlLogger(void *databaseName, int iErrCode, const char *zMsg) {
|
||||
// skip printing this message if it is due to certain types of errors
|
||||
if (iErrCode == SQLITE_CONSTRAINT) return;
|
||||
LOGI("sqlite returned: error code = %d, msg = %s\n", iErrCode, zMsg);
|
||||
}
|
||||
|
||||
// register the logging func on sqlite. needs to be done BEFORE any sqlite3 func is called.
|
||||
static void registerLoggingFunc(const char *path) {
|
||||
static bool loggingFuncSet = false;
|
||||
if (loggingFuncSet) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOGV("Registering sqlite logging func \n");
|
||||
int err = sqlite3_config(SQLITE_CONFIG_LOG, &sqlLogger, (void *)createStr(path));
|
||||
if (err != SQLITE_OK) {
|
||||
LOGE("sqlite_config failed error_code = %d. THIS SHOULD NEVER occur.\n", err);
|
||||
return;
|
||||
}
|
||||
loggingFuncSet = true;
|
||||
}
|
||||
|
||||
int native_status(JNIEnv* env, jobject object, jint operation, jboolean reset)
|
||||
{
|
||||
int value;
|
||||
int highWater;
|
||||
sqlite3 * handle = (sqlite3 *)env->GetLongField(object, offset_db_handle);
|
||||
int status = sqlite3_status(operation, &value, &highWater, reset);
|
||||
if(status != SQLITE_OK){
|
||||
throw_sqlite3_exception(env, handle);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void native_key(JNIEnv* env, jobject object, jbyteArray jKey) {
|
||||
int rc = 0;
|
||||
int index = 0;
|
||||
jsize size = 0;
|
||||
jbyte *key = 0;
|
||||
sqlite3 *handle = NULL;
|
||||
handle = (sqlite3 *)env->GetLongField(object, offset_db_handle);
|
||||
if(handle == NULL){
|
||||
LOGE("env->GetLongField returned NULL when retrieving sqlite3 *\n");
|
||||
}
|
||||
key = env->GetByteArrayElements(jKey, NULL);
|
||||
size = env->GetArrayLength(jKey);
|
||||
if(key == NULL || size == 0) goto done;
|
||||
rc = sqlite3_key(handle, key, size);
|
||||
if(rc != SQLITE_OK) {
|
||||
throw_sqlite3_exception(env, handle);
|
||||
}
|
||||
done:
|
||||
if(key) env->ReleaseByteArrayElements(jKey, key, JNI_ABORT);
|
||||
}
|
||||
|
||||
void native_rekey(JNIEnv* env, jobject object, jbyteArray jKey) {
|
||||
int rc = 0;
|
||||
jsize size = 0;
|
||||
jbyte *key = 0;
|
||||
sqlite3 *handle = NULL;
|
||||
handle = (sqlite3 *)env->GetLongField(object, offset_db_handle);
|
||||
key = env->GetByteArrayElements(jKey, NULL);
|
||||
size = env->GetArrayLength(jKey);
|
||||
if(key == NULL || size == 0) goto done;
|
||||
rc = sqlite3_rekey(handle, key, size);
|
||||
if(rc != SQLITE_OK) {
|
||||
throw_sqlite3_exception(env, handle);
|
||||
}
|
||||
done:
|
||||
if(key) env->ReleaseByteArrayElements(jKey, key, JNI_ABORT);
|
||||
}
|
||||
|
||||
void native_key_mutf8(JNIEnv* env, jobject object, jcharArray jKey) {
|
||||
int rc;
|
||||
int idx;
|
||||
jint releaseElements = 0;
|
||||
jboolean arrayIsCopy;
|
||||
sqlite3 *handle = (sqlite3 *)env->GetLongField(object, offset_db_handle);
|
||||
jsize sz = env->GetArrayLength(jKey);
|
||||
jchar* jKeyChar = env->GetCharArrayElements(jKey, &arrayIsCopy);
|
||||
jstring key = env->NewString(jKeyChar, sz);
|
||||
const char* password = env->GetStringUTFChars(key, JNI_FALSE);
|
||||
int password_sz = env->GetStringUTFLength(key);
|
||||
if(password_sz > 0){
|
||||
rc = sqlite3_key(handle, password, password_sz);
|
||||
if(rc != SQLITE_OK){
|
||||
throw_sqlite3_exception(env, handle);
|
||||
}
|
||||
}
|
||||
env->ReleaseCharArrayElements(jKey, jKeyChar, JNI_ABORT);
|
||||
env->ReleaseStringUTFChars(key, password);
|
||||
}
|
||||
|
||||
void native_rawExecSQL(JNIEnv* env, jobject object, jstring sql)
|
||||
{
|
||||
sqlite3 * handle = (sqlite3 *)env->GetLongField(object, offset_db_handle);
|
||||
char const * sqlCommand = env->GetStringUTFChars(sql, NULL);
|
||||
int status = sqlite3_exec(handle, sqlCommand, NULL, NULL, NULL);
|
||||
env->ReleaseStringUTFChars(sql, sqlCommand);
|
||||
if(status != SQLITE_OK){
|
||||
throw_sqlite3_exception(env, handle);
|
||||
}
|
||||
}
|
||||
|
||||
/* public native void setICURoot(String path); */
|
||||
// void setICURoot(JNIEnv* env, jobject object, jstring ICURoot)
|
||||
// {
|
||||
// char const * ICURootPath = env->GetStringUTFChars(ICURoot, NULL);
|
||||
// setenv("SQLCIPHER_ICU_PREFIX", ICURootPath, 1);
|
||||
// env->ReleaseStringUTFChars(ICURoot, ICURootPath);
|
||||
// }
|
||||
|
||||
|
||||
/* public native void dbopen(String path, int flags, String locale); */
|
||||
void dbopen(JNIEnv* env, jobject object, jstring pathString, jint flags)
|
||||
{
|
||||
int err;
|
||||
sqlite3 * handle = NULL;
|
||||
sqlite3_stmt * statement = NULL;
|
||||
char const * path8 = env->GetStringUTFChars(pathString, NULL);
|
||||
int sqliteFlags;
|
||||
|
||||
// register the logging func on sqlite. needs to be done BEFORE any sqlite3 func is called.
|
||||
registerLoggingFunc(path8);
|
||||
|
||||
// convert our flags into the sqlite flags
|
||||
if (flags & CREATE_IF_NECESSARY) {
|
||||
sqliteFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
|
||||
} else if (flags & OPEN_READONLY) {
|
||||
sqliteFlags = SQLITE_OPEN_READONLY;
|
||||
} else {
|
||||
sqliteFlags = SQLITE_OPEN_READWRITE;
|
||||
}
|
||||
|
||||
err = sqlite3_open_v2(path8, &handle, sqliteFlags, NULL);
|
||||
if (err != SQLITE_OK) {
|
||||
LOGE("sqlite3_open_v2(\"%s\", &handle, %d, NULL) failed\n", path8, sqliteFlags);
|
||||
throw_sqlite3_exception_errcode(env, err, "Could not open database");
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Check that the database is really read/write when that is what we asked for.
|
||||
if ((sqliteFlags & SQLITE_OPEN_READWRITE) && sqlite3_db_readonly(handle, NULL)) {
|
||||
throw_sqlite3_exception(env, handle, "Could not open the database in read/write mode.");
|
||||
goto done;
|
||||
}
|
||||
|
||||
// The soft heap limit prevents the page cache allocations from growing
|
||||
// beyond the given limit, no matter what the max page cache sizes are
|
||||
// set to. The limit does not, as of 3.5.0, affect any other allocations.
|
||||
sqlite3_soft_heap_limit(SQLITE_SOFT_HEAP_LIMIT);
|
||||
|
||||
// Set the default busy handler to retry for 1000ms and then return SQLITE_BUSY
|
||||
err = sqlite3_busy_timeout(handle, 1000 /* ms */);
|
||||
if (err != SQLITE_OK) {
|
||||
LOGE("sqlite3_busy_timeout(handle, 1000) failed for \"%s\"\n", path8);
|
||||
throw_sqlite3_exception(env, handle, "Could not set busy timeout");
|
||||
goto done;
|
||||
}
|
||||
|
||||
#ifdef DB_INTEGRITY_CHECK
|
||||
static const char* integritySql = "pragma integrity_check(1);";
|
||||
err = sqlite3_prepare_v2(handle, integritySql, -1, &statement, NULL);
|
||||
if (err != SQLITE_OK) {
|
||||
LOGE("sqlite_prepare_v2(handle, \"%s\") failed for \"%s\"\n", integritySql, path8);
|
||||
throw_sqlite3_exception(env, handle, "sqlite_prepare_v2(handle, \"pragma integrity_check(1);\") failed");
|
||||
goto done;
|
||||
}
|
||||
|
||||
// first is OK or error message
|
||||
err = sqlite3_step(statement);
|
||||
if (err != SQLITE_ROW) {
|
||||
LOGE("integrity check failed for \"%s\"\n", integritySql, path8);
|
||||
throw_sqlite3_exception(env, handle);
|
||||
goto done;
|
||||
} else {
|
||||
const char *text = (const char*)sqlite3_column_text(statement, 0);
|
||||
if (strcmp(text, "ok") != 0) {
|
||||
LOGE("integrity check failed for \"%s\": %s\n", integritySql, path8, text);
|
||||
jniThrowException(env, "net/sqlcipher/database/SQLiteDatabaseCorruptException", text);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
sqlite3_enable_load_extension(handle, 1);
|
||||
|
||||
LOGV("Opened '%s' - %p\n", path8, handle);
|
||||
env->SetLongField(object, offset_db_handle, (intptr_t)handle);
|
||||
handle = NULL; // The caller owns the handle now.
|
||||
|
||||
done:
|
||||
// Release allocated resources
|
||||
if (path8 != NULL) env->ReleaseStringUTFChars(pathString, path8);
|
||||
if (statement != NULL) sqlite3_finalize(statement);
|
||||
if (handle != NULL) sqlite3_close(handle);
|
||||
}
|
||||
|
||||
static char *getDatabaseName(JNIEnv* env, sqlite3 * handle, jstring databaseName) {
|
||||
char const *path = env->GetStringUTFChars(databaseName, NULL);
|
||||
if (path == NULL) {
|
||||
LOGE("Failure in getDatabaseName(). VM ran out of memory?\n");
|
||||
return NULL; // VM would have thrown OutOfMemoryError
|
||||
}
|
||||
char *dbNameStr = createStr(path);
|
||||
env->ReleaseStringUTFChars(databaseName, path);
|
||||
return dbNameStr;
|
||||
}
|
||||
|
||||
static void sqlTrace(void *databaseName, const char *sql) {
|
||||
LOGI("sql_statement|%s|%s\n", (char *)databaseName, sql);
|
||||
}
|
||||
|
||||
/* public native void enableSqlTracing(); */
|
||||
static void enableSqlTracing(JNIEnv* env, jobject object, jstring databaseName)
|
||||
{
|
||||
sqlite3 * handle = (sqlite3 *)env->GetLongField(object, offset_db_handle);
|
||||
sqlite3_trace(handle, &sqlTrace, (void *)getDatabaseName(env, handle, databaseName));
|
||||
}
|
||||
|
||||
static void sqlProfile(void *databaseName, const char *sql, sqlite3_uint64 tm) {
|
||||
double d = tm/1000000.0;
|
||||
LOGI("elapsedTime4Sql|%s|%.3f ms|%s\n", (char *)databaseName, d, sql);
|
||||
}
|
||||
|
||||
/* public native void enableSqlProfiling(); */
|
||||
static void enableSqlProfiling(JNIEnv* env, jobject object, jstring databaseName)
|
||||
{
|
||||
sqlite3 * handle = (sqlite3 *)env->GetLongField(object, offset_db_handle);
|
||||
sqlite3_profile(handle, &sqlProfile, (void *)getDatabaseName(env, handle, databaseName));
|
||||
}
|
||||
|
||||
|
||||
/* public native void close(); */
|
||||
static void dbclose(JNIEnv* env, jobject object)
|
||||
{
|
||||
sqlite3 * handle = (sqlite3 *)env->GetLongField(object, offset_db_handle);
|
||||
|
||||
if (handle != NULL) {
|
||||
// release the memory associated with the traceFuncArg in enableSqlTracing function
|
||||
void *traceFuncArg = sqlite3_trace(handle, &sqlTrace, NULL);
|
||||
if (traceFuncArg != NULL) {
|
||||
free(traceFuncArg);
|
||||
}
|
||||
// release the memory associated with the traceFuncArg in enableSqlProfiling function
|
||||
traceFuncArg = sqlite3_profile(handle, &sqlProfile, NULL);
|
||||
if (traceFuncArg != NULL) {
|
||||
free(traceFuncArg);
|
||||
}
|
||||
LOGV("Closing database: handle=%p\n", handle);
|
||||
int result = sqlite3_close(handle);
|
||||
if (result == SQLITE_OK) {
|
||||
LOGV("Closed %p\n", handle);
|
||||
env->SetLongField(object, offset_db_handle, 0);
|
||||
} else {
|
||||
// This can happen if sub-objects aren't closed first. Make sure the caller knows.
|
||||
LOGE("sqlite3_close(%p) failed: %d\n", handle, result);
|
||||
throw_sqlite3_exception(env, handle, "sqlite3_close() failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* public native void native_execSQL(String sql); */
|
||||
static void native_execSQL(JNIEnv* env, jobject object, jstring sqlString)
|
||||
{
|
||||
int err;
|
||||
int stepErr;
|
||||
sqlite3_stmt * statement = NULL;
|
||||
sqlite3 * handle = (sqlite3 *)env->GetLongField(object, offset_db_handle);
|
||||
jchar const * sql = env->GetStringChars(sqlString, NULL);
|
||||
jsize sqlLen = env->GetStringLength(sqlString);
|
||||
|
||||
if (sql == NULL || sqlLen == 0) {
|
||||
jniThrowException(env, "java/lang/IllegalArgumentException", "You must supply an SQL string");
|
||||
return;
|
||||
}
|
||||
|
||||
err = sqlite3_prepare16_v2(handle, sql, sqlLen * 2, &statement, NULL);
|
||||
|
||||
env->ReleaseStringChars(sqlString, sql);
|
||||
|
||||
if (err != SQLITE_OK) {
|
||||
char const * sql8 = env->GetStringUTFChars(sqlString, NULL);
|
||||
LOGE("Failure %d (%s) on %p when preparing '%s'.\n", err, sqlite3_errmsg(handle), handle, sql8);
|
||||
throw_sqlite3_exception(env, handle, sql8);
|
||||
env->ReleaseStringUTFChars(sqlString, sql8);
|
||||
return;
|
||||
}
|
||||
|
||||
stepErr = sqlite3_step(statement);
|
||||
err = sqlite3_finalize(statement);
|
||||
|
||||
if (stepErr != SQLITE_DONE) {
|
||||
if (stepErr == SQLITE_ROW) {
|
||||
throw_sqlite3_exception(env, "Queries cannot be performed using execSQL(), use query() instead.");
|
||||
} else {
|
||||
char const * sql8 = env->GetStringUTFChars(sqlString, NULL);
|
||||
LOGE("Failure %d (%s) on %p when executing '%s'\n", err, sqlite3_errmsg(handle), handle, sql8);
|
||||
throw_sqlite3_exception(env, handle, sql8);
|
||||
env->ReleaseStringUTFChars(sqlString, sql8);
|
||||
|
||||
}
|
||||
} else
|
||||
#ifndef DB_LOG_STATEMENTS
|
||||
// IF_LOGV()
|
||||
#endif
|
||||
{
|
||||
char const * sql8 = env->GetStringUTFChars(sqlString, NULL);
|
||||
LOGV("Success on %p when executing '%s'\n", handle, sql8);
|
||||
env->ReleaseStringUTFChars(sqlString, sql8);
|
||||
}
|
||||
}
|
||||
|
||||
/* native long lastInsertRow(); */
|
||||
static jlong lastInsertRow(JNIEnv* env, jobject object)
|
||||
{
|
||||
sqlite3 * handle = (sqlite3 *)env->GetLongField(object, offset_db_handle);
|
||||
|
||||
return sqlite3_last_insert_rowid(handle);
|
||||
}
|
||||
|
||||
/* native int lastChangeCount(); */
|
||||
static jint lastChangeCount(JNIEnv* env, jobject object)
|
||||
{
|
||||
sqlite3 * handle = (sqlite3 *)env->GetLongField(object, offset_db_handle);
|
||||
|
||||
return sqlite3_changes(handle);
|
||||
}
|
||||
|
||||
/* native int native_getDbLookaside(); */
|
||||
static jint native_getDbLookaside(JNIEnv* env, jobject object)
|
||||
{
|
||||
sqlite3 * handle = (sqlite3 *)env->GetLongField(object, offset_db_handle);
|
||||
int pCur = -1;
|
||||
int unused;
|
||||
sqlite3_db_status(handle, SQLITE_DBSTATUS_LOOKASIDE_USED, &pCur, &unused, 0);
|
||||
return pCur;
|
||||
}
|
||||
|
||||
/* set locale in the android_metadata table, install localized collators, and rebuild indexes */
|
||||
// static void native_setLocale(JNIEnv* env, jobject object, jstring localeString, jint flags)
|
||||
// {
|
||||
// if ((flags & NO_LOCALIZED_COLLATORS)) return;
|
||||
|
||||
// int err;
|
||||
// char const* locale8 = env->GetStringUTFChars(localeString, NULL);
|
||||
// sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
|
||||
// sqlite3_stmt* stmt = NULL;
|
||||
// char** meta = NULL;
|
||||
// int rowCount, colCount;
|
||||
// char* dbLocale = NULL;
|
||||
|
||||
// // create the table, if necessary and possible
|
||||
// if (!(flags & OPEN_READONLY)) {
|
||||
// static const char *createSql ="CREATE TABLE IF NOT EXISTS " ANDROID_TABLE " (locale TEXT)";
|
||||
// err = sqlite3_exec(handle, createSql, NULL, NULL, NULL);
|
||||
// if (err != SQLITE_OK) {
|
||||
// LOGE("CREATE TABLE " ANDROID_TABLE " failed\n");
|
||||
// throw_sqlite3_exception(env, handle, "create locale table failed");
|
||||
// goto done;
|
||||
// }
|
||||
// }
|
||||
|
||||
// // try to read from the table
|
||||
// static const char *selectSql = "SELECT locale FROM " ANDROID_TABLE " LIMIT 1";
|
||||
// err = sqlite3_get_table(handle, selectSql, &meta, &rowCount, &colCount, NULL);
|
||||
// if (err != SQLITE_OK) {
|
||||
// LOGE("SELECT locale FROM " ANDROID_TABLE " failed\n");
|
||||
// throw_sqlite3_exception(env, handle, "select locale failed");
|
||||
// goto done;
|
||||
// }
|
||||
|
||||
// dbLocale = (rowCount >= 1) ? meta[colCount] : NULL;
|
||||
|
||||
// if (dbLocale != NULL && !strcmp(dbLocale, locale8)) {
|
||||
// // database locale is the same as the desired locale; set up the collators and go
|
||||
// err = register_localized_collators(handle, locale8, UTF16_STORAGE);
|
||||
// if (err != SQLITE_OK) throw_sqlite3_exception(env, handle);
|
||||
// goto done; // no database changes needed
|
||||
// }
|
||||
|
||||
// if ((flags & OPEN_READONLY)) {
|
||||
// // read-only database, so we're going to have to put up with whatever we got
|
||||
// // For registering new index. Not for modifing the read-only database.
|
||||
// err = register_localized_collators(handle, locale8, UTF16_STORAGE);
|
||||
// if (err != SQLITE_OK) throw_sqlite3_exception(env, handle, "register localized collators failed");
|
||||
// goto done;
|
||||
// }
|
||||
|
||||
// // need to update android_metadata and indexes atomically, so use a transaction...
|
||||
// err = sqlite3_exec(handle, "BEGIN TRANSACTION", NULL, NULL, NULL);
|
||||
// if (err != SQLITE_OK) {
|
||||
// LOGE("BEGIN TRANSACTION failed setting locale\n");
|
||||
// throw_sqlite3_exception(env, handle, "BEGIN TRANSACTION failed setting locale");
|
||||
// goto done;
|
||||
// }
|
||||
|
||||
// err = register_localized_collators(handle, locale8, UTF16_STORAGE);
|
||||
// if (err != SQLITE_OK) {
|
||||
// LOGE("register_localized_collators() failed setting locale\n");
|
||||
// throw_sqlite3_exception(env, handle, "register_localized_collators() failed setting locale");
|
||||
// goto rollback;
|
||||
// }
|
||||
|
||||
// err = sqlite3_exec(handle, "DELETE FROM " ANDROID_TABLE, NULL, NULL, NULL);
|
||||
// if (err != SQLITE_OK) {
|
||||
// LOGE("DELETE failed setting locale\n");
|
||||
// throw_sqlite3_exception(env, handle, "DELETE failed setting locale");
|
||||
// goto rollback;
|
||||
// }
|
||||
|
||||
// static const char *sql = "INSERT INTO " ANDROID_TABLE " (locale) VALUES(?);";
|
||||
// err = sqlite3_prepare_v2(handle, sql, -1, &stmt, NULL);
|
||||
// if (err != SQLITE_OK) {
|
||||
// LOGE("sqlite3_prepare_v2(\"%s\") failed\n", sql);
|
||||
// throw_sqlite3_exception(env, handle, "sqlite3_prepare_v2() failed setting locale");
|
||||
// goto rollback;
|
||||
// }
|
||||
|
||||
// err = sqlite3_bind_text(stmt, 1, locale8, -1, SQLITE_TRANSIENT);
|
||||
// if (err != SQLITE_OK) {
|
||||
// LOGE("sqlite3_bind_text() failed setting locale\n");
|
||||
// throw_sqlite3_exception(env, handle, "sqlite3_bind_text() failed setting locale");
|
||||
// goto rollback;
|
||||
// }
|
||||
|
||||
// err = sqlite3_step(stmt);
|
||||
// if (err != SQLITE_OK && err != SQLITE_DONE) {
|
||||
// LOGE("sqlite3_step(\"%s\") failed setting locale\n", sql);
|
||||
// throw_sqlite3_exception(env, handle, "sqlite3_step() failed setting locale");
|
||||
// goto rollback;
|
||||
// }
|
||||
|
||||
// err = sqlite3_exec(handle, "REINDEX LOCALIZED", NULL, NULL, NULL);
|
||||
// if (err != SQLITE_OK) {
|
||||
// LOGE("REINDEX LOCALIZED failed\n");
|
||||
// throw_sqlite3_exception(env, handle, "REINDEX LOCALIZED failed");
|
||||
// goto rollback;
|
||||
// }
|
||||
|
||||
// // all done, yay!
|
||||
// err = sqlite3_exec(handle, "COMMIT TRANSACTION", NULL, NULL, NULL);
|
||||
// if (err != SQLITE_OK) {
|
||||
// LOGE("COMMIT TRANSACTION failed setting locale\n");
|
||||
// throw_sqlite3_exception(env, handle, "COMMIT TRANSACTION failed setting locale");
|
||||
// goto done;
|
||||
// }
|
||||
|
||||
// rollback:
|
||||
// if (err != SQLITE_OK) {
|
||||
// sqlite3_exec(handle, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
|
||||
// }
|
||||
|
||||
// done:
|
||||
// if (locale8 != NULL) env->ReleaseStringUTFChars(localeString, locale8);
|
||||
// if (stmt != NULL) sqlite3_finalize(stmt);
|
||||
// if (meta != NULL) sqlite3_free_table(meta);
|
||||
// }
|
||||
|
||||
static jint native_releaseMemory(JNIEnv *env, jobject clazz)
|
||||
{
|
||||
// Attempt to release as much memory from the
|
||||
return sqlite3_release_memory(SQLITE_SOFT_HEAP_LIMIT);
|
||||
}
|
||||
|
||||
static JNINativeMethod sMethods[] =
|
||||
{
|
||||
/* name, signature, funcPtr */
|
||||
{"dbopen", "(Ljava/lang/String;I)V", (void *)dbopen},
|
||||
{"dbclose", "()V", (void *)dbclose},
|
||||
{"enableSqlTracing", "(Ljava/lang/String;)V", (void *)enableSqlTracing},
|
||||
{"enableSqlProfiling", "(Ljava/lang/String;)V", (void *)enableSqlProfiling},
|
||||
{"native_execSQL", "(Ljava/lang/String;)V", (void *)native_execSQL},
|
||||
{"lastInsertRow", "()J", (void *)lastInsertRow},
|
||||
{"lastChangeCount", "()I", (void *)lastChangeCount},
|
||||
{"native_getDbLookaside", "()I", (void *)native_getDbLookaside},
|
||||
{"releaseMemory", "()I", (void *)native_releaseMemory},
|
||||
{"native_rawExecSQL", "(Ljava/lang/String;)V", (void *)native_rawExecSQL},
|
||||
{"native_status", "(IZ)I", (void *)native_status},
|
||||
{"key_mutf8", "([C)V", (void *)native_key_mutf8},
|
||||
{"key", "([B)V", (void *)native_key},
|
||||
{"rekey", "([B)V", (void *)native_rekey},
|
||||
};
|
||||
|
||||
int register_android_database_SQLiteDatabase(JNIEnv *env)
|
||||
{
|
||||
jclass clazz;
|
||||
|
||||
clazz = env->FindClass("net/sqlcipher/database/SQLiteDatabase");
|
||||
if (clazz == NULL) {
|
||||
LOGE("Can't find net/sqlcipher/database/SQLiteDatabase\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
offset_db_handle = env->GetFieldID(clazz, "mNativeHandle", "J");
|
||||
if (offset_db_handle == NULL) {
|
||||
LOGE("Can't find SQLiteDatabase.mNativeHandle\n");
|
||||
return -1;
|
||||
}
|
||||
return env->RegisterNatives(clazz, sMethods, NELEM(sMethods));
|
||||
}
|
||||
|
||||
//this code is not executed
|
||||
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
||||
{
|
||||
JNIEnv *env;
|
||||
//gJavaVM = vm;
|
||||
LOGI("JNI_OnLoad called");
|
||||
if (vm->GetEnv((void**) &env, JNI_VERSION_1_2) != JNI_OK) {
|
||||
LOGE("Failed to get the environment using GetEnv()");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOGI("JNI_OnLoad register methods ");
|
||||
|
||||
register_android_database_SQLiteDatabase(env);
|
||||
register_android_database_SQLiteCompiledSql(env);
|
||||
register_android_database_SQLiteQuery(env);
|
||||
register_android_database_SQLiteProgram(env);
|
||||
register_android_database_SQLiteStatement(env);
|
||||
register_android_database_CursorWindow(env);
|
||||
|
||||
//register_android_database_SQLiteDebug(env);
|
||||
|
||||
return JNI_VERSION_1_2;
|
||||
|
||||
}
|
||||
|
||||
/* throw a SQLiteException with a message appropriate for the error in handle */
|
||||
void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle) {
|
||||
throw_sqlite3_exception(env, handle, NULL);
|
||||
}
|
||||
|
||||
/* throw a SQLiteException with the given message */
|
||||
void throw_sqlite3_exception(JNIEnv* env, const char* message) {
|
||||
throw_sqlite3_exception(env, NULL, message);
|
||||
}
|
||||
|
||||
/* throw a SQLiteException with a message appropriate for the error in handle
|
||||
concatenated with the given message
|
||||
*/
|
||||
void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle, const char* message) {
|
||||
if (handle && sqlite3_errcode(handle) != SQLITE_OK) {
|
||||
throw_sqlite3_exception(env, sqlite3_errcode(handle),
|
||||
sqlite3_errmsg(handle), message);
|
||||
} else {
|
||||
// we use SQLITE_OK so that a generic SQLiteException is thrown;
|
||||
// any code not specified in the switch statement below would do.
|
||||
throw_sqlite3_exception(env, SQLITE_OK, "unknown error", message);
|
||||
}
|
||||
}
|
||||
|
||||
/* throw a SQLiteException for a given error code */
|
||||
void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message) {
|
||||
if (errcode == SQLITE_DONE) {
|
||||
throw_sqlite3_exception(env, errcode, NULL, message);
|
||||
} else {
|
||||
char temp[21];
|
||||
sprintf(temp, "error code %d", errcode);
|
||||
throw_sqlite3_exception(env, errcode, temp, message);
|
||||
}
|
||||
}
|
||||
|
||||
/* throw a SQLiteException for a given error code, sqlite3message, and
|
||||
user message
|
||||
*/
|
||||
void throw_sqlite3_exception(JNIEnv* env, int errcode,
|
||||
const char* sqlite3Message, const char* message) {
|
||||
const char* exceptionClass;
|
||||
switch (errcode) {
|
||||
case SQLITE_IOERR:
|
||||
exceptionClass = "net/sqlcipher/database/SQLiteDiskIOException";
|
||||
break;
|
||||
case SQLITE_CORRUPT:
|
||||
exceptionClass = "net/sqlcipher/database/SQLiteDatabaseCorruptException";
|
||||
break;
|
||||
case SQLITE_CONSTRAINT:
|
||||
exceptionClass = "net/sqlcipher/database/SQLiteConstraintException";
|
||||
break;
|
||||
case SQLITE_ABORT:
|
||||
exceptionClass = "net/sqlcipher/database/SQLiteAbortException";
|
||||
break;
|
||||
case SQLITE_DONE:
|
||||
exceptionClass = "net/sqlcipher/database/SQLiteDoneException";
|
||||
break;
|
||||
case SQLITE_FULL:
|
||||
exceptionClass = "net/sqlcipher/database/SQLiteFullException";
|
||||
break;
|
||||
case SQLITE_MISUSE:
|
||||
exceptionClass = "net/sqlcipher/database/SQLiteMisuseException";
|
||||
break;
|
||||
default:
|
||||
exceptionClass = "net/sqlcipher/database/SQLiteException";
|
||||
break;
|
||||
}
|
||||
|
||||
if (sqlite3Message != NULL && message != NULL) {
|
||||
char* fullMessage = (char *)malloc(strlen(sqlite3Message) + strlen(message) + 3);
|
||||
if (fullMessage != NULL) {
|
||||
strcpy(fullMessage, sqlite3Message);
|
||||
strcat(fullMessage, ": ");
|
||||
strcat(fullMessage, message);
|
||||
jniThrowException(env, exceptionClass, fullMessage);
|
||||
free(fullMessage);
|
||||
} else {
|
||||
jniThrowException(env, exceptionClass, sqlite3Message);
|
||||
}
|
||||
} else if (sqlite3Message != NULL) {
|
||||
jniThrowException(env, exceptionClass, sqlite3Message);
|
||||
} else {
|
||||
jniThrowException(env, exceptionClass, message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace sqlcipher
|
||||
|
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <JNIHelp.h>
|
||||
#include <jni.h>
|
||||
#include <utils/misc.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <cutils/mspace.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
// From mem_mspace.c in libsqlite
|
||||
extern "C" mspace sqlite3_get_mspace();
|
||||
|
||||
namespace sqlcipher {
|
||||
|
||||
static jfieldID gMemoryUsedField;
|
||||
static jfieldID gPageCacheOverfloField;
|
||||
static jfieldID gLargestMemAllocField;
|
||||
|
||||
|
||||
#define USE_MSPACE 0
|
||||
|
||||
static void getPagerStats(JNIEnv *env, jobject clazz, jobject statsObj)
|
||||
{
|
||||
int memoryUsed;
|
||||
int pageCacheOverflo;
|
||||
int largestMemAlloc;
|
||||
int unused;
|
||||
|
||||
sqlite3_status(SQLITE_STATUS_MEMORY_USED, &memoryUsed, &unused, 0);
|
||||
sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &unused, &largestMemAlloc, 0);
|
||||
sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &pageCacheOverflo, &unused, 0);
|
||||
env->SetIntField(statsObj, gMemoryUsedField, memoryUsed);
|
||||
env->SetIntField(statsObj, gPageCacheOverfloField, pageCacheOverflo);
|
||||
env->SetIntField(statsObj, gLargestMemAllocField, largestMemAlloc);
|
||||
}
|
||||
|
||||
static jlong getHeapSize(JNIEnv *env, jobject clazz)
|
||||
{
|
||||
#if !NO_MALLINFO
|
||||
struct mallinfo info = mspace_mallinfo(sqlite3_get_mspace());
|
||||
struct mallinfo info = dlmallinfo();
|
||||
return (jlong) info.usmblks;
|
||||
#elif USE_MSPACE
|
||||
mspace space = sqlite3_get_mspace();
|
||||
if (space != 0) {
|
||||
return mspace_footprint(space);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static jlong getHeapAllocatedSize(JNIEnv *env, jobject clazz)
|
||||
{
|
||||
#if !NO_MALLINFO
|
||||
struct mallinfo info = mspace_mallinfo(sqlite3_get_mspace());
|
||||
return (jlong) info.uordblks;
|
||||
#else
|
||||
return sqlite3_memory_used();
|
||||
#endif
|
||||
}
|
||||
|
||||
static jlong getHeapFreeSize(JNIEnv *env, jobject clazz)
|
||||
{
|
||||
#if !NO_MALLINFO
|
||||
struct mallinfo info = mspace_mallinfo(sqlite3_get_mspace());
|
||||
return (jlong) info.fordblks;
|
||||
#else
|
||||
return getHeapSize(env, clazz) - sqlite3_memory_used();
|
||||
#endif
|
||||
}
|
||||
|
||||
static int read_mapinfo(FILE *fp,
|
||||
int *sharedPages, int *privatePages)
|
||||
{
|
||||
char line[1024];
|
||||
int len;
|
||||
int skip;
|
||||
|
||||
unsigned start = 0, size = 0, resident = 0;
|
||||
unsigned shared_clean = 0, shared_dirty = 0;
|
||||
unsigned private_clean = 0, private_dirty = 0;
|
||||
unsigned referenced = 0;
|
||||
|
||||
int isAnon = 0;
|
||||
int isHeap = 0;
|
||||
|
||||
again:
|
||||
skip = 0;
|
||||
|
||||
if(fgets(line, 1024, fp) == 0) return 0;
|
||||
|
||||
len = strlen(line);
|
||||
if (len < 1) return 0;
|
||||
line[--len] = 0;
|
||||
|
||||
/* ignore guard pages */
|
||||
if (line[18] == '-') skip = 1;
|
||||
|
||||
start = strtoul(line, 0, 16);
|
||||
|
||||
if (len > 50 && !strncmp(line + 49, "/tmp/sqlite-heap", strlen("/tmp/sqlite-heap"))) {
|
||||
isHeap = 1;
|
||||
}
|
||||
|
||||
if (fgets(line, 1024, fp) == 0) return 0;
|
||||
if (sscanf(line, "Size: %d kB", &size) != 1) return 0;
|
||||
if (fgets(line, 1024, fp) == 0) return 0;
|
||||
if (sscanf(line, "Rss: %d kB", &resident) != 1) return 0;
|
||||
if (fgets(line, 1024, fp) == 0) return 0;
|
||||
if (sscanf(line, "Shared_Clean: %d kB", &shared_clean) != 1) return 0;
|
||||
if (fgets(line, 1024, fp) == 0) return 0;
|
||||
if (sscanf(line, "Shared_Dirty: %d kB", &shared_dirty) != 1) return 0;
|
||||
if (fgets(line, 1024, fp) == 0) return 0;
|
||||
if (sscanf(line, "Private_Clean: %d kB", &private_clean) != 1) return 0;
|
||||
if (fgets(line, 1024, fp) == 0) return 0;
|
||||
if (sscanf(line, "Private_Dirty: %d kB", &private_dirty) != 1) return 0;
|
||||
if (fgets(line, 1024, fp) == 0) return 0;
|
||||
if (sscanf(line, "Referenced: %d kB", &referenced) != 1) return 0;
|
||||
|
||||
if (skip) {
|
||||
goto again;
|
||||
}
|
||||
|
||||
if (isHeap) {
|
||||
*sharedPages += shared_dirty;
|
||||
*privatePages += private_dirty;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void load_maps(int pid, int *sharedPages, int *privatePages)
|
||||
{
|
||||
char tmp[128];
|
||||
FILE *fp;
|
||||
|
||||
sprintf(tmp, "/proc/%d/smaps", pid);
|
||||
fp = fopen(tmp, "r");
|
||||
if (fp == 0) return;
|
||||
|
||||
while (read_mapinfo(fp, sharedPages, privatePages) != 0) {
|
||||
// Do nothing
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
static void getHeapDirtyPages(JNIEnv *env, jobject clazz, jintArray pages)
|
||||
{
|
||||
int _pages[2];
|
||||
|
||||
_pages[0] = 0;
|
||||
_pages[1] = 0;
|
||||
|
||||
load_maps(getpid(), &_pages[0], &_pages[1]);
|
||||
|
||||
// Convert from kbytes to 4K pages
|
||||
_pages[0] /= 4;
|
||||
_pages[1] /= 4;
|
||||
|
||||
env->SetIntArrayRegion(pages, 0, 2, _pages);
|
||||
}
|
||||
|
||||
/*
|
||||
* JNI registration.
|
||||
*/
|
||||
|
||||
static JNINativeMethod gMethods[] =
|
||||
{
|
||||
{ "getPagerStats", "(Lnet/sqlcipher/database/SQLiteDebug$PagerStats;)V",
|
||||
(void*) getPagerStats },
|
||||
{ "getHeapSize", "()J", (void*) getHeapSize },
|
||||
{ "getHeapAllocatedSize", "()J", (void*) getHeapAllocatedSize },
|
||||
{ "getHeapFreeSize", "()J", (void*) getHeapFreeSize },
|
||||
{ "getHeapDirtyPages", "([I)V", (void*) getHeapDirtyPages },
|
||||
};
|
||||
|
||||
int register_android_database_SQLiteDebug(JNIEnv *env)
|
||||
{
|
||||
jclass clazz;
|
||||
|
||||
clazz = env->FindClass("net/sqlcipher/database/SQLiteDebug$PagerStats");
|
||||
if (clazz == NULL) {
|
||||
LOGE("Can't find net/sqlcipher/database/SQLiteDebug$PagerStats");
|
||||
return -1;
|
||||
}
|
||||
|
||||
gMemoryUsedField = env->GetFieldID(clazz, "memoryUsed", "I");
|
||||
if (gMemoryUsedField == NULL) {
|
||||
LOGE("Can't find memoryUsed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
gLargestMemAllocField = env->GetFieldID(clazz, "largestMemAlloc", "I");
|
||||
if (gLargestMemAllocField == NULL) {
|
||||
LOGE("Can't find largestMemAlloc");
|
||||
return -1;
|
||||
}
|
||||
|
||||
gPageCacheOverfloField = env->GetFieldID(clazz, "pageCacheOverflo", "I");
|
||||
if (gPageCacheOverfloField == NULL) {
|
||||
LOGE("Can't find pageCacheOverflo");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return jniRegisterNativeMethods(env, "net/sqlcipher/database/SQLiteDebug",
|
||||
gMethods, NELEM(gMethods));
|
||||
}
|
||||
|
||||
} // namespace sqlcipher
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* Copyright (C) 2006-2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "Cursor"
|
||||
|
||||
// #include <JNIHelp.h>
|
||||
// #include <android_runtime/AndroidRuntime.h>
|
||||
// #include <utils/Log.h>
|
||||
|
||||
#include <jni.h>
|
||||
#include <sqlite3.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "log.h"
|
||||
#include "jni_elements.h"
|
||||
#include "jni_exception.h"
|
||||
#include "sqlite3_exception.h"
|
||||
|
||||
namespace sqlcipher {
|
||||
|
||||
static jfieldID gHandleField;
|
||||
static jfieldID gStatementField;
|
||||
|
||||
|
||||
#define GET_STATEMENT(env, object) \
|
||||
(sqlite3_stmt *)env->GetLongField(object, gStatementField)
|
||||
#define GET_HANDLE(env, object) \
|
||||
(sqlite3 *)env->GetLongField(object, gHandleField)
|
||||
|
||||
static void native_compile(JNIEnv* env, jobject object, jstring sqlString)
|
||||
{
|
||||
char buf[65];
|
||||
strcpy(buf, "android_database_SQLiteProgram->native_compile() not implemented");
|
||||
throw_sqlite3_exception(env, GET_HANDLE(env, object), buf);
|
||||
return;
|
||||
}
|
||||
|
||||
static void native_bind_null(JNIEnv* env, jobject object,
|
||||
jint index)
|
||||
{
|
||||
int err;
|
||||
sqlite3_stmt * statement = GET_STATEMENT(env, object);
|
||||
|
||||
err = sqlite3_bind_null(statement, index);
|
||||
if (err != SQLITE_OK) {
|
||||
char buf[32];
|
||||
sprintf(buf, "handle %p", statement);
|
||||
throw_sqlite3_exception(env, GET_HANDLE(env, object), buf);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void native_bind_long(JNIEnv* env, jobject object,
|
||||
jint index, jlong value)
|
||||
{
|
||||
int err;
|
||||
sqlite3_stmt * statement = GET_STATEMENT(env, object);
|
||||
|
||||
err = sqlite3_bind_int64(statement, index, value);
|
||||
if (err != SQLITE_OK) {
|
||||
char buf[32];
|
||||
sprintf(buf, "handle %p", statement);
|
||||
throw_sqlite3_exception(env, GET_HANDLE(env, object), buf);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void native_bind_double(JNIEnv* env, jobject object,
|
||||
jint index, jdouble value)
|
||||
{
|
||||
int err;
|
||||
sqlite3_stmt * statement = GET_STATEMENT(env, object);
|
||||
|
||||
err = sqlite3_bind_double(statement, index, value);
|
||||
if (err != SQLITE_OK) {
|
||||
char buf[32];
|
||||
sprintf(buf, "handle %p", statement);
|
||||
throw_sqlite3_exception(env, GET_HANDLE(env, object), buf);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void native_bind_string(JNIEnv* env, jobject object,
|
||||
jint index, jstring sqlString)
|
||||
{
|
||||
int err;
|
||||
jchar const * sql;
|
||||
jsize sqlLen;
|
||||
sqlite3_stmt * statement= GET_STATEMENT(env, object);
|
||||
|
||||
sql = env->GetStringChars(sqlString, NULL);
|
||||
sqlLen = env->GetStringLength(sqlString);
|
||||
err = sqlite3_bind_text16(statement, index, sql, sqlLen * 2, SQLITE_TRANSIENT);
|
||||
env->ReleaseStringChars(sqlString, sql);
|
||||
if (err != SQLITE_OK) {
|
||||
char buf[32];
|
||||
sprintf(buf, "handle %p", statement);
|
||||
throw_sqlite3_exception(env, GET_HANDLE(env, object), buf);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void native_bind_blob(JNIEnv* env, jobject object,
|
||||
jint index, jbyteArray value)
|
||||
{
|
||||
int err;
|
||||
jchar const * sql;
|
||||
jsize sqlLen;
|
||||
sqlite3_stmt * statement= GET_STATEMENT(env, object);
|
||||
|
||||
jint len = env->GetArrayLength(value);
|
||||
jbyte * bytes = env->GetByteArrayElements(value, NULL);
|
||||
|
||||
err = sqlite3_bind_blob(statement, index, bytes, len, SQLITE_TRANSIENT);
|
||||
env->ReleaseByteArrayElements(value, bytes, JNI_ABORT);
|
||||
|
||||
if (err != SQLITE_OK) {
|
||||
char buf[32];
|
||||
sprintf(buf, "statement %p", statement);
|
||||
throw_sqlite3_exception(env, GET_HANDLE(env, object), buf);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void native_clear_bindings(JNIEnv* env, jobject object)
|
||||
{
|
||||
int err;
|
||||
sqlite3_stmt * statement = GET_STATEMENT(env, object);
|
||||
|
||||
err = sqlite3_clear_bindings(statement);
|
||||
if (err != SQLITE_OK) {
|
||||
throw_sqlite3_exception(env, GET_HANDLE(env, object));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void native_finalize(JNIEnv* env, jobject object)
|
||||
{
|
||||
char buf[66];
|
||||
strcpy(buf, "android_database_SQLiteProgram->native_finalize() not implemented");
|
||||
throw_sqlite3_exception(env, GET_HANDLE(env, object), buf);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static JNINativeMethod sMethods[] =
|
||||
{
|
||||
/* name, signature, funcPtr */
|
||||
{"native_bind_null", "(I)V", (void *)native_bind_null},
|
||||
{"native_bind_long", "(IJ)V", (void *)native_bind_long},
|
||||
{"native_bind_double", "(ID)V", (void *)native_bind_double},
|
||||
{"native_bind_string", "(ILjava/lang/String;)V", (void *)native_bind_string},
|
||||
{"native_bind_blob", "(I[B)V", (void *)native_bind_blob},
|
||||
{"native_clear_bindings", "()V", (void *)native_clear_bindings},
|
||||
};
|
||||
|
||||
int register_android_database_SQLiteProgram(JNIEnv * env)
|
||||
{
|
||||
jclass clazz;
|
||||
|
||||
clazz = env->FindClass("net/sqlcipher/database/SQLiteProgram");
|
||||
if (clazz == NULL) {
|
||||
LOGE("Can't find net/sqlcipher/database/SQLiteProgram");
|
||||
return -1;
|
||||
}
|
||||
|
||||
gHandleField = env->GetFieldID(clazz, "nHandle", "J");
|
||||
gStatementField = env->GetFieldID(clazz, "nStatement", "J");
|
||||
|
||||
if (gHandleField == NULL || gStatementField == NULL) {
|
||||
LOGE("Error locating fields");
|
||||
return -1;
|
||||
}
|
||||
return env->RegisterNatives(clazz, sMethods, NELEM(sMethods));
|
||||
}
|
||||
|
||||
|
||||
} // namespace sqlcipher
|
||||
|
|
@ -0,0 +1,395 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "Cursor"
|
||||
|
||||
#include <jni.h>
|
||||
// #include <JNIHelp.h>
|
||||
// #include <android_runtime/AndroidRuntime.h>
|
||||
// #include <utils/Log.h>
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "log.h"
|
||||
#include "jni_elements.h"
|
||||
#include "jni_exception.h"
|
||||
#include "CursorWindow.h"
|
||||
#include "sqlite3_exception.h"
|
||||
|
||||
namespace sqlcipher {
|
||||
|
||||
CursorWindow * get_window_from_object(JNIEnv * env, jobject javaWindow);
|
||||
|
||||
sqlite3_stmt * compile(JNIEnv* env, jobject object,
|
||||
sqlite3 * handle, jstring sqlString);
|
||||
|
||||
static jfieldID gHandleField;
|
||||
static jfieldID gStatementField;
|
||||
|
||||
|
||||
#define GET_STATEMENT(env, object) \
|
||||
(sqlite3_stmt *)env->GetLongField(object, gStatementField)
|
||||
#define GET_HANDLE(env, object) \
|
||||
(sqlite3 *)env->GetLongField(object, gHandleField)
|
||||
|
||||
static int skip_rows(sqlite3_stmt *statement, int maxRows) {
|
||||
int retryCount = 0;
|
||||
for (int i = 0; i < maxRows; i++) {
|
||||
int err = sqlite3_step(statement);
|
||||
if (err == SQLITE_ROW){
|
||||
// do nothing
|
||||
} else if (err == SQLITE_DONE) {
|
||||
return i;
|
||||
} else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) {
|
||||
// The table is locked, retry
|
||||
LOG_WINDOW("Database locked, retrying");
|
||||
if (retryCount > 50) {
|
||||
LOGE("Bailing on database busy rety");
|
||||
break;
|
||||
}
|
||||
// Sleep to give the thread holding the lock a chance to finish
|
||||
usleep(1000);
|
||||
retryCount++;
|
||||
continue;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
LOGD("skip_rows row %d", maxRows);
|
||||
return maxRows;
|
||||
}
|
||||
|
||||
static int finish_program_and_get_row_count(sqlite3_stmt *statement) {
|
||||
int numRows = 0;
|
||||
int retryCount = 0;
|
||||
while (true) {
|
||||
int err = sqlite3_step(statement);
|
||||
if (err == SQLITE_ROW){
|
||||
numRows++;
|
||||
} else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) {
|
||||
// The table is locked, retry
|
||||
LOG_WINDOW("Database locked, retrying");
|
||||
if (retryCount > 50) {
|
||||
LOGE("Bailing on database busy rety");
|
||||
break;
|
||||
}
|
||||
// Sleep to give the thread holding the lock a chance to finish
|
||||
usleep(1000);
|
||||
retryCount++;
|
||||
continue;
|
||||
} else {
|
||||
// no need to throw exception
|
||||
break;
|
||||
}
|
||||
}
|
||||
sqlite3_reset(statement);
|
||||
LOGD("finish_program_and_get_row_count row %d", numRows);
|
||||
return numRows;
|
||||
}
|
||||
|
||||
static jint native_fill_window(JNIEnv* env, jobject object, jobject javaWindow,
|
||||
jint startPos, jint requiredPos,
|
||||
jint offsetParam, jint maxRead, jint lastPos)
|
||||
{
|
||||
int err;
|
||||
sqlite3_stmt * statement = GET_STATEMENT(env, object);
|
||||
int numRows = lastPos;
|
||||
maxRead += lastPos;
|
||||
int numColumns;
|
||||
int retryCount;
|
||||
int boundParams;
|
||||
CursorWindow * window;
|
||||
|
||||
if (statement == NULL) {
|
||||
LOGE("Invalid statement in fillWindow()");
|
||||
jniThrowException(env, "java/lang/IllegalStateException",
|
||||
"Attempting to access a deactivated, closed, or empty cursor");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Only do the binding if there is a valid offsetParam. If no binding needs to be done
|
||||
// offsetParam will be set to 0, an invliad value.
|
||||
if(offsetParam > 0) {
|
||||
// Bind the offset parameter, telling the program which row to start with
|
||||
err = sqlite3_bind_int(statement, offsetParam, startPos);
|
||||
if (err != SQLITE_OK) {
|
||||
LOGE("Unable to bind offset position, offsetParam = %d", offsetParam);
|
||||
jniThrowException(env, "java/lang/IllegalArgumentException",
|
||||
sqlite3_errmsg(GET_HANDLE(env, object)));
|
||||
return 0;
|
||||
}
|
||||
LOG_WINDOW("Bound to startPos %d", startPos);
|
||||
} else {
|
||||
LOG_WINDOW("Not binding to startPos %d", startPos);
|
||||
}
|
||||
|
||||
// Get the native window
|
||||
window = get_window_from_object(env, javaWindow);
|
||||
if (!window) {
|
||||
LOGE("Invalid CursorWindow");
|
||||
jniThrowException(env, "java/lang/IllegalArgumentException",
|
||||
"Bad CursorWindow");
|
||||
return 0;
|
||||
}
|
||||
LOG_WINDOW("Window: numRows = %d, size = %d, freeSpace = %d", window->getNumRows(), window->size(), window->freeSpace());
|
||||
|
||||
numColumns = sqlite3_column_count(statement);
|
||||
if (!window->setNumColumns(numColumns)) {
|
||||
LOGE("Failed to change column count from %d to %d", window->getNumColumns(), numColumns);
|
||||
jniThrowException(env, "java/lang/IllegalStateException", "numColumns mismatch");
|
||||
return 0;
|
||||
}
|
||||
|
||||
retryCount = 0;
|
||||
if (startPos > 0) {
|
||||
int num = skip_rows(statement, startPos);
|
||||
if (num < 0) {
|
||||
throw_sqlite3_exception(env, GET_HANDLE(env, object));
|
||||
return 0;
|
||||
} else if (num < startPos) {
|
||||
LOGE("startPos %d > actual rows %d", startPos, num);
|
||||
return num;
|
||||
}
|
||||
}
|
||||
|
||||
while(startPos != 0 || numRows < maxRead) {
|
||||
err = sqlite3_step(statement);
|
||||
if (err == SQLITE_ROW) {
|
||||
LOG_WINDOW("\nStepped statement %p to row %d", statement, startPos + numRows);
|
||||
retryCount = 0;
|
||||
|
||||
// Allocate a new field directory for the row. This pointer is not reused
|
||||
// since it mey be possible for it to be relocated on a call to alloc() when
|
||||
// the field data is being allocated.
|
||||
{
|
||||
field_slot_t * fieldDir = window->allocRow();
|
||||
if(!fieldDir && (startPos + numRows) < requiredPos) {
|
||||
LOG_WINDOW("Failed to allocate row, resetting window", startPos + numRows);
|
||||
window->clear();
|
||||
window->setNumColumns(numColumns);
|
||||
fieldDir = window->allocRow();
|
||||
LOG_WINDOW("Window reset, row allocated at %p", fieldDir);
|
||||
}
|
||||
if (!fieldDir) {
|
||||
LOGE("Failed allocating fieldDir at startPos %d row %d", startPos, numRows);
|
||||
return startPos + numRows + finish_program_and_get_row_count(statement) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Pack the row into the window
|
||||
int i;
|
||||
bool failed = false;
|
||||
bool reset = false;
|
||||
for (i = 0; i < numColumns; i++) {
|
||||
|
||||
if(reset) {
|
||||
LOG_WINDOW("Reset requested for row %d, likely cursor window not large enough for current row\n",
|
||||
startPos + numRows);
|
||||
if(!failed && (startPos + numRows) < requiredPos) {
|
||||
LOG_WINDOW("Reseting window, previously unable to map required row %d into window\n",
|
||||
requiredPos);
|
||||
i = 0;
|
||||
window->clear();
|
||||
window->setNumColumns(numColumns);
|
||||
field_slot_t * fieldDir = window->allocRow();
|
||||
if(!fieldDir) {
|
||||
LOG_WINDOW("Failed to allocate row in reset, bailing\n");
|
||||
jniThrowException(env, "net/sqlcipher/RowAllocationException",
|
||||
"Failed to allocate row in reset within native_fill_window");
|
||||
} else {
|
||||
LOG_WINDOW("Allocated row in reset set\n");
|
||||
}
|
||||
} else {
|
||||
LOG_WINDOW("Bailing from reset, requested row %d already mapped in cursor window\n",
|
||||
startPos + numRows);
|
||||
return startPos + numRows + finish_program_and_get_row_count(statement) + 1;
|
||||
}
|
||||
failed = true;
|
||||
reset = false;
|
||||
}
|
||||
|
||||
int type = sqlite3_column_type(statement, i);
|
||||
if (type == SQLITE_TEXT) {
|
||||
// TEXT data
|
||||
uint8_t const * text = (uint8_t const *)sqlite3_column_text16(statement, i);
|
||||
size_t size = sqlite3_column_bytes16(statement, i);
|
||||
int offset = window->alloc(size);
|
||||
if (!offset) {
|
||||
window->freeLastRow();
|
||||
LOGE("Failed allocating %u bytes for text/blob at %d,%d", size,
|
||||
startPos + numRows, i);
|
||||
reset = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
window->copyIn(offset, text, size);
|
||||
|
||||
// This must be updated after the call to alloc(), since that
|
||||
// may move the field around in the window
|
||||
field_slot_t * fieldSlot = window->getFieldSlot(numRows, i);
|
||||
fieldSlot->type = FIELD_TYPE_STRING;
|
||||
fieldSlot->data.buffer.offset = offset;
|
||||
fieldSlot->data.buffer.size = size;
|
||||
|
||||
LOG_WINDOW("%d,%d is TEXT with %u bytes", startPos + numRows, i, size);
|
||||
} else if (type == SQLITE_INTEGER) {
|
||||
// INTEGER data
|
||||
int64_t value = sqlite3_column_int64(statement, i);
|
||||
if (!window->putLong(numRows, i, value)) {
|
||||
window->freeLastRow();
|
||||
LOGE("Failed allocating space for a long in column %d", i);
|
||||
reset = true;
|
||||
continue;
|
||||
}
|
||||
LOG_WINDOW("%d,%d is INTEGER 0x%016llx", startPos + numRows, i, value);
|
||||
} else if (type == SQLITE_FLOAT) {
|
||||
// FLOAT data
|
||||
double value = sqlite3_column_double(statement, i);
|
||||
if (!window->putDouble(numRows, i, value)) {
|
||||
window->freeLastRow();
|
||||
LOGE("Failed allocating space for a double in column %d", i);
|
||||
reset = true;
|
||||
continue;
|
||||
}
|
||||
LOG_WINDOW("%d,%d is FLOAT %lf", startPos + numRows, i, value);
|
||||
} else if (type == SQLITE_BLOB) {
|
||||
// BLOB data
|
||||
uint8_t const * blob = (uint8_t const *)sqlite3_column_blob(statement, i);
|
||||
size_t size = sqlite3_column_bytes16(statement, i);
|
||||
int offset = window->alloc(size);
|
||||
if (!offset) {
|
||||
window->freeLastRow();
|
||||
LOGE("Failed allocating %u bytes for blob at %d,%d", size,
|
||||
startPos + numRows, i);
|
||||
reset = true;
|
||||
continue;
|
||||
}
|
||||
window->copyIn(offset, blob, size);
|
||||
// This must be updated after the call to alloc(), since that
|
||||
// may move the field around in the window
|
||||
field_slot_t * fieldSlot = window->getFieldSlot(numRows, i);
|
||||
fieldSlot->type = FIELD_TYPE_BLOB;
|
||||
fieldSlot->data.buffer.offset = offset;
|
||||
fieldSlot->data.buffer.size = size;
|
||||
|
||||
LOG_WINDOW("%d,%d is Blob with %u bytes @ %d", startPos + numRows, i, size, offset);
|
||||
} else if (type == SQLITE_NULL) {
|
||||
// NULL field
|
||||
window->putNull(numRows, i);
|
||||
|
||||
LOG_WINDOW("%d,%d is NULL", startPos + numRows, i);
|
||||
} else {
|
||||
// Unknown data
|
||||
LOGE("Unknown column type when filling database window");
|
||||
throw_sqlite3_exception(env, "Unknown column type when filling window");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i < numColumns) {
|
||||
// Not all the fields fit in the window
|
||||
// Unknown data error happened
|
||||
break;
|
||||
}
|
||||
|
||||
// Mark the row as complete in the window
|
||||
numRows++;
|
||||
} else if (err == SQLITE_DONE) {
|
||||
// All rows processed, bail
|
||||
LOG_WINDOW("Processed all rows");
|
||||
break;
|
||||
} else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) {
|
||||
// The table is locked, retry
|
||||
LOG_WINDOW("Database locked, retrying");
|
||||
if (retryCount > 50) {
|
||||
LOGE("Bailing on database busy rety");
|
||||
break;
|
||||
}
|
||||
|
||||
// Sleep to give the thread holding the lock a chance to finish
|
||||
usleep(1000);
|
||||
|
||||
retryCount++;
|
||||
continue;
|
||||
} else {
|
||||
throw_sqlite3_exception(env, GET_HANDLE(env, object));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_WINDOW("Resetting statement %p after fetching %d rows in %d bytes\n\n\n\n", statement,
|
||||
numRows, window->size() - window->freeSpace());
|
||||
// LOGI("Filled window with %d rows in %d bytes", numRows, window->size() - window->freeSpace());
|
||||
if (err == SQLITE_ROW) {
|
||||
return -1;
|
||||
} else {
|
||||
sqlite3_reset(statement);
|
||||
return startPos + numRows;
|
||||
}
|
||||
}
|
||||
|
||||
static jint native_column_count(JNIEnv* env, jobject object)
|
||||
{
|
||||
sqlite3_stmt * statement = GET_STATEMENT(env, object);
|
||||
|
||||
return sqlite3_column_count(statement);
|
||||
}
|
||||
|
||||
static jstring native_column_name(JNIEnv* env, jobject object, jint columnIndex)
|
||||
{
|
||||
sqlite3_stmt * statement = GET_STATEMENT(env, object);
|
||||
char const * name;
|
||||
|
||||
name = sqlite3_column_name(statement, columnIndex);
|
||||
|
||||
return env->NewStringUTF(name);
|
||||
}
|
||||
|
||||
|
||||
static JNINativeMethod sMethods[] =
|
||||
{
|
||||
/* name, signature, funcPtr */
|
||||
{"native_fill_window", "(Lnet/sqlcipher/CursorWindow;IIIII)I", (void *)native_fill_window},
|
||||
{"native_column_count", "()I", (void*)native_column_count},
|
||||
{"native_column_name", "(I)Ljava/lang/String;", (void *)native_column_name},
|
||||
};
|
||||
|
||||
|
||||
int register_android_database_SQLiteQuery(JNIEnv * env)
|
||||
{
|
||||
jclass clazz;
|
||||
|
||||
clazz = env->FindClass("net/sqlcipher/database/SQLiteQuery");
|
||||
if (clazz == NULL) {
|
||||
LOGE("Can't find net/sqlcipher/database/SQLiteQuery");
|
||||
return -1;
|
||||
}
|
||||
|
||||
gHandleField = env->GetFieldID(clazz, "nHandle", "J");
|
||||
gStatementField = env->GetFieldID(clazz, "nStatement", "J");
|
||||
|
||||
if (gHandleField == NULL || gStatementField == NULL) {
|
||||
LOGE("Error locating fields");
|
||||
return -1;
|
||||
}
|
||||
return env->RegisterNatives(clazz, sMethods, NELEM(sMethods));
|
||||
}
|
||||
|
||||
|
||||
} // namespace sqlcipher
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
/* //device/libs/android_runtime/android_database_SQLiteCursor.cpp
|
||||
**
|
||||
** Copyright 2006, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "Cursor"
|
||||
|
||||
#include <jni.h>
|
||||
#include <sqlite3.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "jni_elements.h"
|
||||
#include "sqlite3_exception.h"
|
||||
|
||||
namespace sqlcipher {
|
||||
|
||||
|
||||
sqlite3_stmt * compile(JNIEnv* env, jobject object,
|
||||
sqlite3 * handle, jstring sqlString);
|
||||
|
||||
static jfieldID gHandleField;
|
||||
static jfieldID gStatementField;
|
||||
|
||||
|
||||
#define GET_STATEMENT(env, object) \
|
||||
(sqlite3_stmt *)env->GetLongField(object, gStatementField)
|
||||
#define GET_HANDLE(env, object) \
|
||||
(sqlite3 *)env->GetLongField(object, gHandleField)
|
||||
|
||||
|
||||
static void native_execute(JNIEnv* env, jobject object)
|
||||
{
|
||||
int err;
|
||||
sqlite3 * handle = GET_HANDLE(env, object);
|
||||
sqlite3_stmt * statement = GET_STATEMENT(env, object);
|
||||
|
||||
// Execute the statement
|
||||
err = sqlite3_step(statement);
|
||||
|
||||
// Throw an exception if an error occured
|
||||
if (err != SQLITE_DONE) {
|
||||
throw_sqlite3_exception_errcode(env, err, sqlite3_errmsg(handle));
|
||||
}
|
||||
|
||||
// Reset the statment so it's ready to use again
|
||||
sqlite3_reset(statement);
|
||||
}
|
||||
|
||||
static jlong native_1x1_long(JNIEnv* env, jobject object)
|
||||
{
|
||||
int err;
|
||||
sqlite3 * handle = GET_HANDLE(env, object);
|
||||
sqlite3_stmt * statement = GET_STATEMENT(env, object);
|
||||
jlong value = -1;
|
||||
|
||||
// Execute the statement
|
||||
err = sqlite3_step(statement);
|
||||
|
||||
// Handle the result
|
||||
if (err == SQLITE_ROW) {
|
||||
// No errors, read the data and return it
|
||||
value = sqlite3_column_int64(statement, 0);
|
||||
} else {
|
||||
throw_sqlite3_exception_errcode(env, err, sqlite3_errmsg(handle));
|
||||
}
|
||||
|
||||
// Reset the statment so it's ready to use again
|
||||
sqlite3_reset(statement);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static jstring native_1x1_string(JNIEnv* env, jobject object)
|
||||
{
|
||||
int err;
|
||||
sqlite3 * handle = GET_HANDLE(env, object);
|
||||
sqlite3_stmt * statement = GET_STATEMENT(env, object);
|
||||
jstring value = NULL;
|
||||
|
||||
// Execute the statement
|
||||
err = sqlite3_step(statement);
|
||||
|
||||
// Handle the result
|
||||
if (err == SQLITE_ROW) {
|
||||
// No errors, read the data and return it
|
||||
//char const * text = (char const *)sqlite3_column_text(statement, 0);
|
||||
|
||||
const jchar *str = 0;
|
||||
jint strlength = 0;
|
||||
str = (const jchar*) sqlite3_column_text16(statement, 0);
|
||||
strlength = sqlite3_column_bytes16(statement, 0) / sizeof(jchar);
|
||||
value = str ? env->NewString(str, strlength) : NULL;
|
||||
} else {
|
||||
throw_sqlite3_exception_errcode(env, err, sqlite3_errmsg(handle));
|
||||
}
|
||||
|
||||
// Reset the statment so it's ready to use again
|
||||
sqlite3_reset(statement);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
static JNINativeMethod sMethods[] =
|
||||
{
|
||||
/* name, signature, funcPtr */
|
||||
{"native_execute", "()V", (void *)native_execute},
|
||||
{"native_1x1_long", "()J", (void *)native_1x1_long},
|
||||
{"native_1x1_string", "()Ljava/lang/String;", (void *)native_1x1_string},
|
||||
};
|
||||
|
||||
|
||||
int register_android_database_SQLiteStatement(JNIEnv * env)
|
||||
{
|
||||
jclass clazz;
|
||||
|
||||
clazz = env->FindClass("net/sqlcipher/database/SQLiteStatement");
|
||||
if (clazz == NULL) {
|
||||
LOGE("Can't find net/sqlcipher/database/SQLiteStatement");
|
||||
return -1;
|
||||
}
|
||||
|
||||
gHandleField = env->GetFieldID(clazz, "nHandle", "J");
|
||||
gStatementField = env->GetFieldID(clazz, "nStatement", "J");
|
||||
|
||||
if (gHandleField == NULL || gStatementField == NULL) {
|
||||
LOGE("Error locating fields");
|
||||
return -1;
|
||||
}
|
||||
return env->RegisterNatives(clazz, sMethods, NELEM(sMethods));
|
||||
}
|
||||
|
||||
|
||||
} // namespace sqlcipher
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/* //device/libs/include/android_runtime/sqlite3_exception.h
|
||||
**
|
||||
** Copyright 2007, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
#include <jni.h>
|
||||
/* #include <JNIHelp.h> */
|
||||
/* #include <android_runtime/AndroidRuntime.h> */
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
namespace sqlcipher {
|
||||
|
||||
int register_android_database_SQLiteDatabase(JNIEnv *env);
|
||||
|
||||
int register_android_database_SQLiteCompiledSql(JNIEnv * env);
|
||||
|
||||
int register_android_database_SQLiteQuery(JNIEnv * env);
|
||||
|
||||
int register_android_database_SQLiteProgram(JNIEnv * env);
|
||||
|
||||
int register_android_database_SQLiteStatement(JNIEnv * env);
|
||||
|
||||
int register_android_database_SQLiteDebug(JNIEnv *env);
|
||||
|
||||
int register_android_database_CursorWindow(JNIEnv *env);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/* //device/libs/include/android_runtime/sqlite3_exception.h
|
||||
**
|
||||
** Copyright 2007, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _SQLITE3_EXCEPTION_H
|
||||
#define _SQLITE3_EXCEPTION_H 1
|
||||
|
||||
#include <jni.h>
|
||||
/* #include <JNIHelp.h> */
|
||||
/* #include <android_runtime/AndroidRuntime.h> */
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
namespace sqlcipher {
|
||||
|
||||
/* throw a SQLiteException with a message appropriate for the error in handle */
|
||||
void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle);
|
||||
|
||||
/* throw a SQLiteException with the given message */
|
||||
void throw_sqlite3_exception(JNIEnv* env, const char* message);
|
||||
|
||||
/* throw a SQLiteException with a message appropriate for the error in handle
|
||||
concatenated with the given message
|
||||
*/
|
||||
void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle, const char* message);
|
||||
|
||||
/* throw a SQLiteException for a given error code */
|
||||
void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message);
|
||||
|
||||
void throw_sqlite3_exception(JNIEnv* env, int errcode,
|
||||
const char* sqlite3Message, const char* message);
|
||||
}
|
||||
|
||||
#endif // _SQLITE3_EXCEPTION_H
|
||||
Binary file not shown.
|
|
@ -0,0 +1,627 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.database.CharArrayBuffer;
|
||||
import android.database.ContentObservable;
|
||||
import android.database.DataSetObservable;
|
||||
import android.database.DataSetObserver;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Config;
|
||||
import android.util.Log;
|
||||
|
||||
|
||||
/**
|
||||
* This is an abstract cursor class that handles a lot of the common code
|
||||
* that all cursors need to deal with and is provided for convenience reasons.
|
||||
*/
|
||||
public abstract class AbstractCursor implements android.database.CrossProcessCursor, net.sqlcipher.Cursor {
|
||||
private static final String TAG = "Cursor";
|
||||
|
||||
DataSetObservable mDataSetObservable = new DataSetObservable();
|
||||
ContentObservable mContentObservable = new ContentObservable();
|
||||
|
||||
private Bundle mExtras = Bundle.EMPTY;
|
||||
|
||||
/* -------------------------------------------------------- */
|
||||
/* These need to be implemented by subclasses */
|
||||
abstract public int getCount();
|
||||
|
||||
abstract public String[] getColumnNames();
|
||||
|
||||
abstract public String getString(int column);
|
||||
abstract public short getShort(int column);
|
||||
abstract public int getInt(int column);
|
||||
abstract public long getLong(int column);
|
||||
abstract public float getFloat(int column);
|
||||
abstract public double getDouble(int column);
|
||||
abstract public boolean isNull(int column);
|
||||
|
||||
abstract public int getType(int column);
|
||||
|
||||
// TODO implement getBlob in all cursor types
|
||||
public byte[] getBlob(int column) {
|
||||
throw new UnsupportedOperationException("getBlob is not supported");
|
||||
}
|
||||
/* -------------------------------------------------------- */
|
||||
/* Methods that may optionally be implemented by subclasses */
|
||||
|
||||
/**
|
||||
* returns a pre-filled window, return NULL if no such window
|
||||
*/
|
||||
public CursorWindow getWindow() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getColumnCount() {
|
||||
return getColumnNames().length;
|
||||
}
|
||||
|
||||
public void deactivate() {
|
||||
deactivateInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void deactivateInternal() {
|
||||
if (mSelfObserver != null) {
|
||||
mContentResolver.unregisterContentObserver(mSelfObserver);
|
||||
mSelfObserverRegistered = false;
|
||||
}
|
||||
mDataSetObservable.notifyInvalidated();
|
||||
}
|
||||
|
||||
public boolean requery() {
|
||||
if (mSelfObserver != null && mSelfObserverRegistered == false) {
|
||||
|
||||
mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver);
|
||||
mSelfObserverRegistered = true;
|
||||
}
|
||||
mDataSetObservable.notifyChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return mClosed;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
mClosed = true;
|
||||
mContentObservable.unregisterAll();
|
||||
deactivateInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean commitUpdates(Map<? extends Long,? extends Map<String,Object>> values) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean deleteRow() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called every time the cursor is successfully scrolled
|
||||
* to a new position, giving the subclass a chance to update any state it
|
||||
* may have. If it returns false the move function will also do so and the
|
||||
* cursor will scroll to the beforeFirst position.
|
||||
*
|
||||
* @param oldPosition the position that we're moving from
|
||||
* @param newPosition the position that we're moving to
|
||||
* @return true if the move is successful, false otherwise
|
||||
*/
|
||||
public boolean onMove(int oldPosition, int newPosition) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
|
||||
// Default implementation, uses getString
|
||||
String result = getString(columnIndex);
|
||||
if (result != null) {
|
||||
char[] data = buffer.data;
|
||||
if (data == null || data.length < result.length()) {
|
||||
buffer.data = result.toCharArray();
|
||||
} else {
|
||||
result.getChars(0, result.length(), data, 0);
|
||||
}
|
||||
buffer.sizeCopied = result.length();
|
||||
} else {
|
||||
buffer.sizeCopied = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------- */
|
||||
/* Implementation */
|
||||
public AbstractCursor() {
|
||||
mPos = -1;
|
||||
mRowIdColumnIndex = -1;
|
||||
mCurrentRowID = null;
|
||||
mUpdatedRows = new HashMap<Long, Map<String, Object>>();
|
||||
}
|
||||
|
||||
public final int getPosition() {
|
||||
return mPos;
|
||||
}
|
||||
|
||||
public final boolean moveToPosition(int position) {
|
||||
// Make sure position isn't past the end of the cursor
|
||||
final int count = getCount();
|
||||
if (position >= count) {
|
||||
mPos = count;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure position isn't before the beginning of the cursor
|
||||
if (position < 0) {
|
||||
mPos = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for no-op moves, and skip the rest of the work for them
|
||||
if (position == mPos) {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean result = onMove(mPos, position);
|
||||
if (result == false) {
|
||||
mPos = -1;
|
||||
} else {
|
||||
mPos = position;
|
||||
if (mRowIdColumnIndex != -1) {
|
||||
mCurrentRowID = Long.valueOf(getLong(mRowIdColumnIndex));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy data from cursor to CursorWindow
|
||||
* @param position start position of data
|
||||
* @param window
|
||||
*/
|
||||
public void fillWindow(int position, android.database.CursorWindow window) {
|
||||
DatabaseUtils.cursorFillWindow(this, position, window);
|
||||
}
|
||||
|
||||
public final boolean move(int offset) {
|
||||
return moveToPosition(mPos + offset);
|
||||
}
|
||||
|
||||
public final boolean moveToFirst() {
|
||||
return moveToPosition(0);
|
||||
}
|
||||
|
||||
public final boolean moveToLast() {
|
||||
return moveToPosition(getCount() - 1);
|
||||
}
|
||||
|
||||
public final boolean moveToNext() {
|
||||
return moveToPosition(mPos + 1);
|
||||
}
|
||||
|
||||
public final boolean moveToPrevious() {
|
||||
return moveToPosition(mPos - 1);
|
||||
}
|
||||
|
||||
public final boolean isFirst() {
|
||||
return mPos == 0 && getCount() != 0;
|
||||
}
|
||||
|
||||
public final boolean isLast() {
|
||||
int cnt = getCount();
|
||||
return mPos == (cnt - 1) && cnt != 0;
|
||||
}
|
||||
|
||||
public final boolean isBeforeFirst() {
|
||||
if (getCount() == 0) {
|
||||
return true;
|
||||
}
|
||||
return mPos == -1;
|
||||
}
|
||||
|
||||
public final boolean isAfterLast() {
|
||||
if (getCount() == 0) {
|
||||
return true;
|
||||
}
|
||||
return mPos == getCount();
|
||||
}
|
||||
|
||||
public int getColumnIndex(String columnName) {
|
||||
// Hack according to bug 903852
|
||||
final int periodIndex = columnName.lastIndexOf('.');
|
||||
if (periodIndex != -1) {
|
||||
Exception e = new Exception();
|
||||
Log.e(TAG, "requesting column name with table name -- " + columnName, e);
|
||||
columnName = columnName.substring(periodIndex + 1);
|
||||
}
|
||||
|
||||
String columnNames[] = getColumnNames();
|
||||
int length = columnNames.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (columnNames[i].equalsIgnoreCase(columnName)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.LOGV) {
|
||||
if (getCount() > 0) {
|
||||
Log.w("AbstractCursor", "Unknown column " + columnName);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int getColumnIndexOrThrow(String columnName) {
|
||||
final int index = getColumnIndex(columnName);
|
||||
if (index < 0) {
|
||||
throw new IllegalArgumentException("column '" + columnName + "' does not exist");
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
public String getColumnName(int columnIndex) {
|
||||
return getColumnNames()[columnIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean updateBlob(int columnIndex, byte[] value) {
|
||||
return update(columnIndex, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean updateString(int columnIndex, String value) {
|
||||
return update(columnIndex, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean updateShort(int columnIndex, short value) {
|
||||
return update(columnIndex, Short.valueOf(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean updateInt(int columnIndex, int value) {
|
||||
return update(columnIndex, Integer.valueOf(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean updateLong(int columnIndex, long value) {
|
||||
return update(columnIndex, Long.valueOf(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean updateFloat(int columnIndex, float value) {
|
||||
return update(columnIndex, Float.valueOf(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean updateDouble(int columnIndex, double value) {
|
||||
return update(columnIndex, Double.valueOf(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean updateToNull(int columnIndex) {
|
||||
return update(columnIndex, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean update(int columnIndex, Object obj) {
|
||||
if (!supportsUpdates()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Long.valueOf() returns null sometimes!
|
||||
// Long rowid = Long.valueOf(getLong(mRowIdColumnIndex));
|
||||
Long rowid = Long.valueOf(getLong(mRowIdColumnIndex));
|
||||
if (rowid == null) {
|
||||
throw new IllegalStateException("null rowid. mRowIdColumnIndex = " + mRowIdColumnIndex);
|
||||
}
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
Map<String, Object> row = mUpdatedRows.get(rowid);
|
||||
if (row == null) {
|
||||
row = new HashMap<String, Object>();
|
||||
mUpdatedRows.put(rowid, row);
|
||||
}
|
||||
row.put(getColumnNames()[columnIndex], obj);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if there are pending updates that have not yet been committed.
|
||||
*
|
||||
* @return <code>true</code> if there are pending updates that have not yet been committed.
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean hasUpdates() {
|
||||
synchronized(mUpdatedRows) {
|
||||
return mUpdatedRows.size() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public void abortUpdates() {
|
||||
synchronized(mUpdatedRows) {
|
||||
mUpdatedRows.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean commitUpdates() {
|
||||
return commitUpdates(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
public boolean supportsUpdates() {
|
||||
return mRowIdColumnIndex != -1;
|
||||
}
|
||||
|
||||
public void registerContentObserver(ContentObserver observer) {
|
||||
mContentObservable.registerObserver(observer);
|
||||
}
|
||||
|
||||
public void unregisterContentObserver(ContentObserver observer) {
|
||||
// cursor will unregister all observers when it close
|
||||
if (!mClosed) {
|
||||
mContentObservable.unregisterObserver(observer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is hidden until the data set change model has been re-evaluated.
|
||||
* @hide
|
||||
*/
|
||||
protected void notifyDataSetChange() {
|
||||
mDataSetObservable.notifyChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is hidden until the data set change model has been re-evaluated.
|
||||
* @hide
|
||||
*/
|
||||
protected DataSetObservable getDataSetObservable() {
|
||||
return mDataSetObservable;
|
||||
|
||||
}
|
||||
public void registerDataSetObserver(DataSetObserver observer) {
|
||||
mDataSetObservable.registerObserver(observer);
|
||||
|
||||
}
|
||||
|
||||
public void unregisterDataSetObserver(DataSetObserver observer) {
|
||||
mDataSetObservable.unregisterObserver(observer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must call this method when they finish committing updates to notify all
|
||||
* observers.
|
||||
*
|
||||
* @param selfChange
|
||||
*/
|
||||
protected void onChange(boolean selfChange) {
|
||||
synchronized (mSelfObserverLock) {
|
||||
mContentObservable.dispatchChange(selfChange);
|
||||
if (mNotifyUri != null && selfChange) {
|
||||
mContentResolver.notifyChange(mNotifyUri, mSelfObserver);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies a content URI to watch for changes.
|
||||
*
|
||||
* @param cr The content resolver from the caller's context.
|
||||
* @param notifyUri The URI to watch for changes. This can be a
|
||||
* specific row URI, or a base URI for a whole class of content.
|
||||
*/
|
||||
public void setNotificationUri(ContentResolver cr, Uri notifyUri) {
|
||||
synchronized (mSelfObserverLock) {
|
||||
mNotifyUri = notifyUri;
|
||||
mContentResolver = cr;
|
||||
if (mSelfObserver != null) {
|
||||
mContentResolver.unregisterContentObserver(mSelfObserver);
|
||||
}
|
||||
mSelfObserver = new SelfContentObserver(this);
|
||||
mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver);
|
||||
mSelfObserverRegistered = true;
|
||||
}
|
||||
}
|
||||
|
||||
public Uri getNotificationUri() {
|
||||
return mNotifyUri;
|
||||
}
|
||||
|
||||
public boolean getWantsAllOnMoveCalls() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setExtras(Bundle extras) {
|
||||
mExtras = (extras == null) ? Bundle.EMPTY : extras;
|
||||
}
|
||||
|
||||
public Bundle getExtras() {
|
||||
return mExtras;
|
||||
}
|
||||
|
||||
public Bundle respond(Bundle extras) {
|
||||
return Bundle.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function returns true if the field has been updated and is
|
||||
* used in conjunction with {@link #getUpdatedField} to allow subclasses to
|
||||
* support reading uncommitted updates. NOTE: This function and
|
||||
* {@link #getUpdatedField} should be called together inside of a
|
||||
* block synchronized on mUpdatedRows.
|
||||
*
|
||||
* @param columnIndex the column index of the field to check
|
||||
* @return true if the field has been updated, false otherwise
|
||||
*/
|
||||
protected boolean isFieldUpdated(int columnIndex) {
|
||||
if (mRowIdColumnIndex != -1 && mUpdatedRows.size() > 0) {
|
||||
Map<String, Object> updates = mUpdatedRows.get(mCurrentRowID);
|
||||
if (updates != null && updates.containsKey(getColumnNames()[columnIndex])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function returns the uncommitted updated value for the field
|
||||
* at columnIndex. NOTE: This function and {@link #isFieldUpdated} should
|
||||
* be called together inside of a block synchronized on mUpdatedRows.
|
||||
*
|
||||
* @param columnIndex the column index of the field to retrieve
|
||||
* @return the updated value
|
||||
*/
|
||||
protected Object getUpdatedField(int columnIndex) {
|
||||
Map<String, Object> updates = mUpdatedRows.get(mCurrentRowID);
|
||||
return updates.get(getColumnNames()[columnIndex]);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function throws CursorIndexOutOfBoundsException if
|
||||
* the cursor position is out of bounds. Subclass implementations of
|
||||
* the get functions should call this before attempting
|
||||
* to retrieve data.
|
||||
*
|
||||
* @throws CursorIndexOutOfBoundsException
|
||||
*/
|
||||
protected void checkPosition() {
|
||||
if (-1 == mPos || getCount() == mPos) {
|
||||
throw new CursorIndexOutOfBoundsException(mPos, getCount());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() {
|
||||
if (mSelfObserver != null && mSelfObserverRegistered == true) {
|
||||
mContentResolver.unregisterContentObserver(mSelfObserver);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cursors use this class to track changes others make to their URI.
|
||||
*/
|
||||
protected static class SelfContentObserver extends ContentObserver {
|
||||
WeakReference<AbstractCursor> mCursor;
|
||||
|
||||
public SelfContentObserver(AbstractCursor cursor) {
|
||||
super(null);
|
||||
mCursor = new WeakReference<AbstractCursor>(cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deliverSelfNotifications() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
AbstractCursor cursor = mCursor.get();
|
||||
if (cursor != null) {
|
||||
cursor.onChange(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This HashMap contains a mapping from Long rowIDs to another Map
|
||||
* that maps from String column names to new values. A NULL value means to
|
||||
* remove an existing value, and all numeric values are in their class
|
||||
* forms, i.e. Integer, Long, Float, etc.
|
||||
*/
|
||||
protected HashMap<Long, Map<String, Object>> mUpdatedRows;
|
||||
|
||||
/**
|
||||
* This must be set to the index of the row ID column by any
|
||||
* subclass that wishes to support updates.
|
||||
*/
|
||||
protected int mRowIdColumnIndex;
|
||||
|
||||
protected int mPos;
|
||||
|
||||
/**
|
||||
* If {@link #mRowIdColumnIndex} is not -1 this contains contains the value of
|
||||
* the column at {@link #mRowIdColumnIndex} for the current row this cursor is
|
||||
* pointing at.
|
||||
*/
|
||||
protected Long mCurrentRowID;
|
||||
protected ContentResolver mContentResolver;
|
||||
protected boolean mClosed = false;
|
||||
private Uri mNotifyUri;
|
||||
private ContentObserver mSelfObserver;
|
||||
final private Object mSelfObserverLock = new Object();
|
||||
private boolean mSelfObserverRegistered;
|
||||
}
|
||||
|
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher;
|
||||
|
||||
import android.database.CharArrayBuffer;
|
||||
|
||||
/**
|
||||
* A base class for Cursors that store their data in {@link CursorWindow}s.
|
||||
*/
|
||||
public abstract class AbstractWindowedCursor extends AbstractCursor
|
||||
{
|
||||
@Override
|
||||
public byte[] getBlob(int columnIndex)
|
||||
{
|
||||
checkPosition();
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (isFieldUpdated(columnIndex)) {
|
||||
return (byte[])getUpdatedField(columnIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return mWindow.getBlob(mPos, columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(int columnIndex)
|
||||
{
|
||||
checkPosition();
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (isFieldUpdated(columnIndex)) {
|
||||
return (String)getUpdatedField(columnIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return mWindow.getString(mPos, columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer)
|
||||
{
|
||||
checkPosition();
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (isFieldUpdated(columnIndex)) {
|
||||
super.copyStringToBuffer(columnIndex, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
mWindow.copyStringToBuffer(mPos, columnIndex, buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getShort(int columnIndex)
|
||||
{
|
||||
checkPosition();
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (isFieldUpdated(columnIndex)) {
|
||||
Number value = (Number)getUpdatedField(columnIndex);
|
||||
return value.shortValue();
|
||||
}
|
||||
}
|
||||
|
||||
return mWindow.getShort(mPos, columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(int columnIndex)
|
||||
{
|
||||
checkPosition();
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (isFieldUpdated(columnIndex)) {
|
||||
Number value = (Number)getUpdatedField(columnIndex);
|
||||
return value.intValue();
|
||||
}
|
||||
}
|
||||
|
||||
return mWindow.getInt(mPos, columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(int columnIndex)
|
||||
{
|
||||
checkPosition();
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (isFieldUpdated(columnIndex)) {
|
||||
Number value = (Number)getUpdatedField(columnIndex);
|
||||
return value.longValue();
|
||||
}
|
||||
}
|
||||
|
||||
return mWindow.getLong(mPos, columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFloat(int columnIndex)
|
||||
{
|
||||
checkPosition();
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (isFieldUpdated(columnIndex)) {
|
||||
Number value = (Number)getUpdatedField(columnIndex);
|
||||
return value.floatValue();
|
||||
}
|
||||
}
|
||||
|
||||
return mWindow.getFloat(mPos, columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(int columnIndex)
|
||||
{
|
||||
checkPosition();
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (isFieldUpdated(columnIndex)) {
|
||||
Number value = (Number)getUpdatedField(columnIndex);
|
||||
return value.doubleValue();
|
||||
}
|
||||
}
|
||||
|
||||
return mWindow.getDouble(mPos, columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNull(int columnIndex)
|
||||
{
|
||||
checkPosition();
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (isFieldUpdated(columnIndex)) {
|
||||
return getUpdatedField(columnIndex) == null;
|
||||
}
|
||||
}
|
||||
|
||||
return mWindow.isNull(mPos, columnIndex);
|
||||
}
|
||||
|
||||
public boolean isBlob(int columnIndex)
|
||||
{
|
||||
checkPosition();
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (isFieldUpdated(columnIndex)) {
|
||||
Object object = getUpdatedField(columnIndex);
|
||||
return object == null || object instanceof byte[];
|
||||
}
|
||||
}
|
||||
|
||||
return mWindow.isBlob(mPos, columnIndex);
|
||||
}
|
||||
|
||||
public boolean isString(int columnIndex)
|
||||
{
|
||||
checkPosition();
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (isFieldUpdated(columnIndex)) {
|
||||
Object object = getUpdatedField(columnIndex);
|
||||
return object == null || object instanceof String;
|
||||
}
|
||||
}
|
||||
|
||||
return mWindow.isString(mPos, columnIndex);
|
||||
}
|
||||
|
||||
public boolean isLong(int columnIndex)
|
||||
{
|
||||
checkPosition();
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (isFieldUpdated(columnIndex)) {
|
||||
Object object = getUpdatedField(columnIndex);
|
||||
return object != null && (object instanceof Integer || object instanceof Long);
|
||||
}
|
||||
}
|
||||
|
||||
return mWindow.isLong(mPos, columnIndex);
|
||||
}
|
||||
|
||||
public boolean isFloat(int columnIndex)
|
||||
{
|
||||
checkPosition();
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (isFieldUpdated(columnIndex)) {
|
||||
Object object = getUpdatedField(columnIndex);
|
||||
return object != null && (object instanceof Float || object instanceof Double);
|
||||
}
|
||||
}
|
||||
|
||||
return mWindow.isFloat(mPos, columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getType(int columnIndex) {
|
||||
checkPosition();
|
||||
return mWindow.getType(mPos, columnIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkPosition()
|
||||
{
|
||||
super.checkPosition();
|
||||
|
||||
if (mWindow == null) {
|
||||
throw new StaleDataException("Access closed cursor");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CursorWindow getWindow() {
|
||||
return mWindow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a new cursor window to cursor, usually set a remote cursor window
|
||||
* @param window cursor window
|
||||
*/
|
||||
public void setWindow(CursorWindow window) {
|
||||
if (mWindow != null) {
|
||||
mWindow.close();
|
||||
}
|
||||
mWindow = window;
|
||||
}
|
||||
|
||||
public boolean hasWindow() {
|
||||
return mWindow != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This needs be updated in {@link #onMove} by subclasses, and
|
||||
* needs to be set to NULL when the contents of the cursor change.
|
||||
*/
|
||||
protected CursorWindow mWindow;
|
||||
}
|
||||
|
|
@ -0,0 +1,440 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher;
|
||||
|
||||
import android.os.Binder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.IBinder;
|
||||
import android.os.Parcel;
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Native implementation of the bulk cursor. This is only for use in implementing
|
||||
* IPC, application code should use the Cursor interface.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public abstract class BulkCursorNative extends Binder implements IBulkCursor
|
||||
{
|
||||
public BulkCursorNative()
|
||||
{
|
||||
attachInterface(this, descriptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast a Binder object into a content resolver interface, generating
|
||||
* a proxy if needed.
|
||||
*/
|
||||
static public IBulkCursor asInterface(IBinder obj)
|
||||
{
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
IBulkCursor in = (IBulkCursor)obj.queryLocalInterface(descriptor);
|
||||
if (in != null) {
|
||||
return in;
|
||||
}
|
||||
|
||||
return new BulkCursorProxy(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
|
||||
throws RemoteException {
|
||||
try {
|
||||
switch (code) {
|
||||
case GET_CURSOR_WINDOW_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
int startPos = data.readInt();
|
||||
CursorWindow window = getWindow(startPos);
|
||||
if (window == null) {
|
||||
reply.writeInt(0);
|
||||
return true;
|
||||
}
|
||||
reply.writeNoException();
|
||||
reply.writeInt(1);
|
||||
window.writeToParcel(reply, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
case COUNT_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
int count = count();
|
||||
reply.writeNoException();
|
||||
reply.writeInt(count);
|
||||
return true;
|
||||
}
|
||||
|
||||
case GET_COLUMN_NAMES_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
String[] columnNames = getColumnNames();
|
||||
reply.writeNoException();
|
||||
reply.writeInt(columnNames.length);
|
||||
int length = columnNames.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
reply.writeString(columnNames[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case DEACTIVATE_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
deactivate();
|
||||
reply.writeNoException();
|
||||
return true;
|
||||
}
|
||||
|
||||
case CLOSE_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
close();
|
||||
reply.writeNoException();
|
||||
return true;
|
||||
}
|
||||
|
||||
case REQUERY_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
IContentObserver observer =
|
||||
IContentObserver.Stub.asInterface(data.readStrongBinder());
|
||||
CursorWindow window = CursorWindow.CREATOR.createFromParcel(data);
|
||||
int count = requery(observer, window);
|
||||
reply.writeNoException();
|
||||
reply.writeInt(count);
|
||||
reply.writeBundle(getExtras());
|
||||
return true;
|
||||
}
|
||||
|
||||
case UPDATE_ROWS_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
// TODO - what ClassLoader should be passed to readHashMap?
|
||||
// TODO - switch to Bundle
|
||||
HashMap<Long, Map<String, Object>> values = data.readHashMap(null);
|
||||
boolean result = updateRows(values);
|
||||
reply.writeNoException();
|
||||
reply.writeInt((result == true ? 1 : 0));
|
||||
return true;
|
||||
}
|
||||
|
||||
case DELETE_ROW_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
int position = data.readInt();
|
||||
boolean result = deleteRow(position);
|
||||
reply.writeNoException();
|
||||
reply.writeInt((result == true ? 1 : 0));
|
||||
return true;
|
||||
}
|
||||
|
||||
case ON_MOVE_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
int position = data.readInt();
|
||||
onMove(position);
|
||||
reply.writeNoException();
|
||||
return true;
|
||||
}
|
||||
|
||||
case WANTS_ON_MOVE_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
boolean result = getWantsAllOnMoveCalls();
|
||||
reply.writeNoException();
|
||||
reply.writeInt(result ? 1 : 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
case GET_EXTRAS_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
Bundle extras = getExtras();
|
||||
reply.writeNoException();
|
||||
reply.writeBundle(extras);
|
||||
return true;
|
||||
}
|
||||
|
||||
case RESPOND_TRANSACTION: {
|
||||
data.enforceInterface(IBulkCursor.descriptor);
|
||||
Bundle extras = data.readBundle(getClass().getClassLoader());
|
||||
Bundle returnExtras = respond(extras);
|
||||
reply.writeNoException();
|
||||
reply.writeBundle(returnExtras);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
DatabaseUtils.writeExceptionToParcel(reply, e);
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onTransact(code, data, reply, flags);
|
||||
}
|
||||
|
||||
public IBinder asBinder()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final class BulkCursorProxy implements IBulkCursor {
|
||||
private IBinder mRemote;
|
||||
private Bundle mExtras;
|
||||
|
||||
public BulkCursorProxy(IBinder remote)
|
||||
{
|
||||
mRemote = remote;
|
||||
mExtras = null;
|
||||
}
|
||||
|
||||
public IBinder asBinder()
|
||||
{
|
||||
return mRemote;
|
||||
}
|
||||
|
||||
public CursorWindow getWindow(int startPos) throws RemoteException
|
||||
{
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
|
||||
data.writeInt(startPos);
|
||||
|
||||
mRemote.transact(GET_CURSOR_WINDOW_TRANSACTION, data, reply, 0);
|
||||
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
CursorWindow window = null;
|
||||
if (reply.readInt() == 1) {
|
||||
window = CursorWindow.newFromParcel(reply);
|
||||
}
|
||||
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
public void onMove(int position) throws RemoteException {
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
|
||||
data.writeInt(position);
|
||||
|
||||
mRemote.transact(ON_MOVE_TRANSACTION, data, reply, 0);
|
||||
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
}
|
||||
|
||||
public int count() throws RemoteException
|
||||
{
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
|
||||
boolean result = mRemote.transact(COUNT_TRANSACTION, data, reply, 0);
|
||||
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
int count;
|
||||
if (result == false) {
|
||||
count = -1;
|
||||
} else {
|
||||
count = reply.readInt();
|
||||
}
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
return count;
|
||||
}
|
||||
|
||||
public String[] getColumnNames() throws RemoteException
|
||||
{
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
|
||||
mRemote.transact(GET_COLUMN_NAMES_TRANSACTION, data, reply, 0);
|
||||
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
String[] columnNames = null;
|
||||
int numColumns = reply.readInt();
|
||||
columnNames = new String[numColumns];
|
||||
for (int i = 0; i < numColumns; i++) {
|
||||
columnNames[i] = reply.readString();
|
||||
}
|
||||
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
return columnNames;
|
||||
}
|
||||
|
||||
public void deactivate() throws RemoteException
|
||||
{
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
|
||||
mRemote.transact(DEACTIVATE_TRANSACTION, data, reply, 0);
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
}
|
||||
|
||||
public void close() throws RemoteException
|
||||
{
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
|
||||
mRemote.transact(CLOSE_TRANSACTION, data, reply, 0);
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
}
|
||||
|
||||
public int requery(IContentObserver observer, CursorWindow window) throws RemoteException {
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
|
||||
data.writeStrongInterface(observer);
|
||||
window.writeToParcel(data, 0);
|
||||
|
||||
boolean result = mRemote.transact(REQUERY_TRANSACTION, data, reply, 0);
|
||||
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
int count;
|
||||
if (!result) {
|
||||
count = -1;
|
||||
} else {
|
||||
count = reply.readInt();
|
||||
mExtras = reply.readBundle(getClass().getClassLoader());
|
||||
}
|
||||
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public boolean updateRows(Map values) throws RemoteException
|
||||
{
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
|
||||
data.writeMap(values);
|
||||
|
||||
mRemote.transact(UPDATE_ROWS_TRANSACTION, data, reply, 0);
|
||||
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
boolean result = (reply.readInt() == 1 ? true : false);
|
||||
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean deleteRow(int position) throws RemoteException
|
||||
{
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
|
||||
data.writeInt(position);
|
||||
|
||||
mRemote.transact(DELETE_ROW_TRANSACTION, data, reply, 0);
|
||||
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
boolean result = (reply.readInt() == 1 ? true : false);
|
||||
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean getWantsAllOnMoveCalls() throws RemoteException {
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
|
||||
mRemote.transact(WANTS_ON_MOVE_TRANSACTION, data, reply, 0);
|
||||
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
int result = reply.readInt();
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
public Bundle getExtras() throws RemoteException {
|
||||
if (mExtras == null) {
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
|
||||
mRemote.transact(GET_EXTRAS_TRANSACTION, data, reply, 0);
|
||||
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
mExtras = reply.readBundle(getClass().getClassLoader());
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
}
|
||||
return mExtras;
|
||||
}
|
||||
|
||||
public Bundle respond(Bundle extras) throws RemoteException {
|
||||
Parcel data = Parcel.obtain();
|
||||
Parcel reply = Parcel.obtain();
|
||||
|
||||
data.writeInterfaceToken(IBulkCursor.descriptor);
|
||||
|
||||
data.writeBundle(extras);
|
||||
|
||||
mRemote.transact(RESPOND_TRANSACTION, data, reply, 0);
|
||||
|
||||
DatabaseUtils.readExceptionFromParcel(reply);
|
||||
|
||||
Bundle returnExtras = reply.readBundle(getClass().getClassLoader());
|
||||
data.recycle();
|
||||
reply.recycle();
|
||||
return returnExtras;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,319 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import android.database.CharArrayBuffer;
|
||||
import android.database.ContentObserver;
|
||||
import android.database.DataSetObserver;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Adapts an {@link IBulkCursor} to a {@link Cursor} for use in the local
|
||||
* process.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor {
|
||||
private static final String TAG = "BulkCursor";
|
||||
|
||||
private SelfContentObserver mObserverBridge;
|
||||
private IBulkCursor mBulkCursor;
|
||||
private int mCount;
|
||||
private String[] mColumns;
|
||||
private boolean mWantsAllOnMoveCalls;
|
||||
|
||||
public void set(IBulkCursor bulkCursor) {
|
||||
mBulkCursor = bulkCursor;
|
||||
|
||||
try {
|
||||
mCount = mBulkCursor.count();
|
||||
mWantsAllOnMoveCalls = mBulkCursor.getWantsAllOnMoveCalls();
|
||||
|
||||
// Search for the rowID column index and set it for our parent
|
||||
mColumns = mBulkCursor.getColumnNames();
|
||||
mRowIdColumnIndex = findRowIdColumnIndex(mColumns);
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "Setup failed because the remote process is dead");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Version of set() that does fewer Binder calls if the caller
|
||||
* already knows BulkCursorToCursorAdaptor's properties.
|
||||
*/
|
||||
public void set(IBulkCursor bulkCursor, int count, int idIndex) {
|
||||
mBulkCursor = bulkCursor;
|
||||
mColumns = null; // lazily retrieved
|
||||
mCount = count;
|
||||
mRowIdColumnIndex = idIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns column index of "_id" column, or -1 if not found.
|
||||
*/
|
||||
public static int findRowIdColumnIndex(String[] columnNames) {
|
||||
int length = columnNames.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (columnNames[i].equals("_id")) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a SelfDataChangeOberserver that can be sent to a remote
|
||||
* process to receive change notifications over IPC.
|
||||
*
|
||||
* @return A SelfContentObserver hooked up to this Cursor
|
||||
*/
|
||||
public synchronized IContentObserver getObserver() {
|
||||
if (mObserverBridge == null) {
|
||||
mObserverBridge = new SelfContentObserver(this);
|
||||
}
|
||||
return null;//mObserverBridge.getContentObserver(); //TODO nf fix this
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMove(int oldPosition, int newPosition) {
|
||||
try {
|
||||
// Make sure we have the proper window
|
||||
if (mWindow != null) {
|
||||
if (newPosition < mWindow.getStartPosition() ||
|
||||
newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) {
|
||||
mWindow = mBulkCursor.getWindow(newPosition);
|
||||
} else if (mWantsAllOnMoveCalls) {
|
||||
mBulkCursor.onMove(newPosition);
|
||||
}
|
||||
} else {
|
||||
mWindow = mBulkCursor.getWindow(newPosition);
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
// We tried to get a window and failed
|
||||
Log.e(TAG, "Unable to get window because the remote process is dead");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Couldn't obtain a window, something is wrong
|
||||
if (mWindow == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
// This will call onInvalidated(), so make sure to do it before calling release,
|
||||
// which is what actually makes the data set invalid.
|
||||
super.deactivate();
|
||||
|
||||
try {
|
||||
mBulkCursor.deactivate();
|
||||
} catch (RemoteException ex) {
|
||||
Log.w(TAG, "Remote process exception when deactivating");
|
||||
}
|
||||
mWindow = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
try {
|
||||
mBulkCursor.close();
|
||||
} catch (RemoteException ex) {
|
||||
Log.w(TAG, "Remote process exception when closing");
|
||||
}
|
||||
mWindow = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requery() {
|
||||
try {
|
||||
int oldCount = mCount;
|
||||
//TODO get the window from a pool somewhere to avoid creating the memory dealer
|
||||
mCount = mBulkCursor.requery(getObserver(), new CursorWindow(
|
||||
false /* the window will be accessed across processes */));
|
||||
if (mCount != -1) {
|
||||
mPos = -1;
|
||||
mWindow = null;
|
||||
|
||||
// super.requery() will call onChanged. Do it here instead of relying on the
|
||||
// observer from the far side so that observers can see a correct value for mCount
|
||||
// when responding to onChanged.
|
||||
super.requery();
|
||||
return true;
|
||||
} else {
|
||||
deactivate();
|
||||
return false;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Log.e(TAG, "Unable to requery because the remote process exception " + ex.getMessage());
|
||||
deactivate();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
@Override
|
||||
public boolean deleteRow() {
|
||||
try {
|
||||
boolean result = mBulkCursor.deleteRow(mPos);
|
||||
if (result != false) {
|
||||
// The window contains the old value, discard it
|
||||
mWindow = null;
|
||||
|
||||
// Fix up the position
|
||||
mCount = mBulkCursor.count();
|
||||
if (mPos < mCount) {
|
||||
int oldPos = mPos;
|
||||
mPos = -1;
|
||||
moveToPosition(oldPos);
|
||||
} else {
|
||||
mPos = mCount;
|
||||
}
|
||||
|
||||
// Send the change notification
|
||||
onChange(true);
|
||||
}
|
||||
return result;
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "Unable to delete row because the remote process is dead");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getColumnNames() {
|
||||
if (mColumns == null) {
|
||||
try {
|
||||
mColumns = mBulkCursor.getColumnNames();
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "Unable to fetch column names because the remote process is dead");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return mColumns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
@Override
|
||||
public boolean commitUpdates(Map<? extends Long,
|
||||
? extends Map<String,Object>> additionalValues) {
|
||||
if (!supportsUpdates()) {
|
||||
Log.e(TAG, "commitUpdates not supported on this cursor, did you include the _id column?");
|
||||
return false;
|
||||
}
|
||||
|
||||
synchronized(mUpdatedRows) {
|
||||
if (additionalValues != null) {
|
||||
mUpdatedRows.putAll(additionalValues);
|
||||
}
|
||||
|
||||
if (mUpdatedRows.size() <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
boolean result = mBulkCursor.updateRows(mUpdatedRows);
|
||||
|
||||
if (result == true) {
|
||||
mUpdatedRows.clear();
|
||||
|
||||
// Send the change notification
|
||||
onChange(true);
|
||||
}
|
||||
return result;
|
||||
} catch (RemoteException ex) {
|
||||
Log.e(TAG, "Unable to commit updates because the remote process is dead");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle getExtras() {
|
||||
try {
|
||||
return mBulkCursor.getExtras();
|
||||
} catch (RemoteException e) {
|
||||
// This should never happen because the system kills processes that are using remote
|
||||
// cursors when the provider process is killed.
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle respond(Bundle extras) {
|
||||
try {
|
||||
return mBulkCursor.respond(extras);
|
||||
} catch (RemoteException e) {
|
||||
// the system kills processes that are using remote cursors when the provider process
|
||||
// is killed, but this can still happen if this is being called from the system process,
|
||||
// so, better to log and return an empty bundle.
|
||||
Log.w(TAG, "respond() threw RemoteException, returning an empty bundle.", e);
|
||||
return Bundle.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerContentObserver(ContentObserver observer) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerDataSetObserver(DataSetObserver observer) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterContentObserver(ContentObserver observer) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterDataSetObserver(DataSetObserver observer) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package net.sqlcipher;
|
||||
|
||||
import android.database.CrossProcessCursor;
|
||||
import android.database.CursorWindow;
|
||||
|
||||
public class CrossProcessCursorWrapper extends CursorWrapper implements CrossProcessCursor {
|
||||
|
||||
public CrossProcessCursorWrapper(Cursor cursor) {
|
||||
super(cursor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CursorWindow getWindow() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillWindow(int position, CursorWindow window) {
|
||||
DatabaseUtils.cursorFillWindow(this, position, window);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMove(int oldPosition, int newPosition) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher;
|
||||
|
||||
/**
|
||||
* Extension of android.database.Cursor to support getType() for API < 11.
|
||||
*/
|
||||
public interface Cursor extends android.database.Cursor {
|
||||
/*
|
||||
* Values returned by {@link #getType(int)}.
|
||||
* These should be consistent with the corresponding types defined in CursorWindow.h
|
||||
*/
|
||||
/** Value returned by {@link #getType(int)} if the specified column is null */
|
||||
static final int FIELD_TYPE_NULL = 0;
|
||||
|
||||
/** Value returned by {@link #getType(int)} if the specified column type is integer */
|
||||
static final int FIELD_TYPE_INTEGER = 1;
|
||||
|
||||
/** Value returned by {@link #getType(int)} if the specified column type is float */
|
||||
static final int FIELD_TYPE_FLOAT = 2;
|
||||
|
||||
/** Value returned by {@link #getType(int)} if the specified column type is string */
|
||||
static final int FIELD_TYPE_STRING = 3;
|
||||
|
||||
/** Value returned by {@link #getType(int)} if the specified column type is blob */
|
||||
static final int FIELD_TYPE_BLOB = 4;
|
||||
|
||||
/**
|
||||
* Returns data type of the given column's value.
|
||||
* The preferred type of the column is returned but the data may be converted to other types
|
||||
* as documented in the get-type methods such as {@link #getInt(int)}, {@link #getFloat(int)}
|
||||
* etc.
|
||||
*<p>
|
||||
* Returned column types are
|
||||
* <ul>
|
||||
* <li>{@link #FIELD_TYPE_NULL}</li>
|
||||
* <li>{@link #FIELD_TYPE_INTEGER}</li>
|
||||
* <li>{@link #FIELD_TYPE_FLOAT}</li>
|
||||
* <li>{@link #FIELD_TYPE_STRING}</li>
|
||||
* <li>{@link #FIELD_TYPE_BLOB}</li>
|
||||
*</ul>
|
||||
*</p>
|
||||
*
|
||||
* @param columnIndex the zero-based index of the target column.
|
||||
* @return column value type
|
||||
*/
|
||||
int getType(int columnIndex);
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher;
|
||||
|
||||
/**
|
||||
* An exception indicating that a cursor is out of bounds.
|
||||
*/
|
||||
public class CursorIndexOutOfBoundsException extends IndexOutOfBoundsException {
|
||||
|
||||
public CursorIndexOutOfBoundsException(int index, int size) {
|
||||
super("Index " + index + " requested, with a size of " + size);
|
||||
}
|
||||
|
||||
public CursorIndexOutOfBoundsException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,662 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher;
|
||||
|
||||
import android.database.CharArrayBuffer;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.database.sqlite.SQLiteClosable;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.Process;
|
||||
import android.util.Log;
|
||||
import android.util.SparseIntArray;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import net.sqlcipher.CursorWindowAllocation;
|
||||
import net.sqlcipher.DefaultCursorWindowAllocation;
|
||||
|
||||
/**
|
||||
* A buffer containing multiple cursor rows.
|
||||
*/
|
||||
public class CursorWindow extends android.database.CursorWindow implements Parcelable {
|
||||
/** The pointer to the native window class */
|
||||
@SuppressWarnings("unused")
|
||||
|
||||
/** The pointer to the native window class. set by the native methods in
|
||||
* android_database_CursorWindow.cpp
|
||||
*/
|
||||
private long nWindow;
|
||||
private int mStartPos;
|
||||
private int mRequiredPos;
|
||||
|
||||
private static CursorWindowAllocation allocation = new DefaultCursorWindowAllocation();
|
||||
|
||||
public static void setCursorWindowAllocation(CursorWindowAllocation value){
|
||||
allocation = value;
|
||||
}
|
||||
|
||||
public static CursorWindowAllocation getCursorWindowAllocation() {
|
||||
return allocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new empty window.
|
||||
*
|
||||
* @param localWindow true if this window will be used in this process only
|
||||
*/
|
||||
public CursorWindow(boolean localWindow) {
|
||||
super(localWindow);
|
||||
mStartPos = 0;
|
||||
if(allocation == null){
|
||||
allocation = new DefaultCursorWindowAllocation();
|
||||
}
|
||||
native_init(localWindow,
|
||||
allocation.getInitialAllocationSize(),
|
||||
allocation.getGrowthPaddingSize(),
|
||||
allocation.getMaxAllocationSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the starting position of this window within the entire
|
||||
* Cursor's result set.
|
||||
*
|
||||
* @return the starting position of this window within the entire
|
||||
* Cursor's result set.
|
||||
*/
|
||||
public int getStartPosition() {
|
||||
return mStartPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the start position of cursor window
|
||||
* @param pos
|
||||
*/
|
||||
public void setStartPosition(int pos) {
|
||||
mStartPos = pos;
|
||||
}
|
||||
|
||||
public int getRequiredPosition(){
|
||||
return mRequiredPos;
|
||||
}
|
||||
|
||||
public void setRequiredPosition(int pos) {
|
||||
mRequiredPos = pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of rows in this window.
|
||||
*
|
||||
* @return the number of rows in this window.
|
||||
*/
|
||||
public int getNumRows() {
|
||||
acquireReference();
|
||||
try {
|
||||
return getNumRows_native();
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native int getNumRows_native();
|
||||
/**
|
||||
* Set number of Columns
|
||||
* @param columnNum
|
||||
* @return true if success
|
||||
*/
|
||||
public boolean setNumColumns(int columnNum) {
|
||||
acquireReference();
|
||||
try {
|
||||
return setNumColumns_native(columnNum);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native boolean setNumColumns_native(int columnNum);
|
||||
|
||||
/**
|
||||
* Allocate a row in cursor window
|
||||
* @return false if cursor window is out of memory
|
||||
*/
|
||||
public boolean allocRow(){
|
||||
acquireReference();
|
||||
try {
|
||||
return allocRow_native();
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native boolean allocRow_native();
|
||||
|
||||
/**
|
||||
* Free the last row
|
||||
*/
|
||||
public void freeLastRow(){
|
||||
acquireReference();
|
||||
try {
|
||||
freeLastRow_native();
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native void freeLastRow_native();
|
||||
|
||||
/**
|
||||
* copy byte array to cursor window
|
||||
* @param value
|
||||
* @param row
|
||||
* @param col
|
||||
* @return false if fail to copy
|
||||
*/
|
||||
public boolean putBlob(byte[] value, int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return putBlob_native(value, row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native boolean putBlob_native(byte[] value, int row, int col);
|
||||
|
||||
/**
|
||||
* Copy String to cursor window
|
||||
* @param value
|
||||
* @param row
|
||||
* @param col
|
||||
* @return false if fail to copy
|
||||
*/
|
||||
public boolean putString(String value, int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return putString_native(value, row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native boolean putString_native(String value, int row, int col);
|
||||
|
||||
/**
|
||||
* Copy integer to cursor window
|
||||
* @param value
|
||||
* @param row
|
||||
* @param col
|
||||
* @return false if fail to copy
|
||||
*/
|
||||
public boolean putLong(long value, int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return putLong_native(value, row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native boolean putLong_native(long value, int row, int col);
|
||||
|
||||
|
||||
/**
|
||||
* Copy double to cursor window
|
||||
* @param value
|
||||
* @param row
|
||||
* @param col
|
||||
* @return false if fail to copy
|
||||
*/
|
||||
public boolean putDouble(double value, int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return putDouble_native(value, row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native boolean putDouble_native(double value, int row, int col);
|
||||
|
||||
/**
|
||||
* Set the [row, col] value to NULL
|
||||
* @param row
|
||||
* @param col
|
||||
* @return false if fail to copy
|
||||
*/
|
||||
public boolean putNull(int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return putNull_native(row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native boolean putNull_native(int row, int col);
|
||||
|
||||
|
||||
/**
|
||||
* Returns {@code true} if given field is {@code NULL}.
|
||||
*
|
||||
* @param row the row to read from, row - getStartPosition() being the actual row in the window
|
||||
* @param col the column to read from
|
||||
* @return {@code true} if given field is {@code NULL}
|
||||
*/
|
||||
public boolean isNull(int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return isNull_native(row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native boolean isNull_native(int row, int col);
|
||||
|
||||
/**
|
||||
* Returns a byte array for the given field.
|
||||
*
|
||||
* @param row the row to read from, row - getStartPosition() being the actual row in the window
|
||||
* @param col the column to read from
|
||||
* @return a String value for the given field
|
||||
*/
|
||||
public byte[] getBlob(int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return getBlob_native(row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value at (<code>row</code>, <code>col</code>) as a <code>byte</code> array.
|
||||
*
|
||||
* <p>If the value is null, then <code>null</code> is returned. If the
|
||||
* type of column <code>col</code> is a string type, then the result
|
||||
* is the array of bytes that make up the internal representation of the
|
||||
* string value. If the type of column <code>col</code> is integral or floating-point,
|
||||
* then an {@link SQLiteException} is thrown.
|
||||
*/
|
||||
private native byte[] getBlob_native(int row, int col);
|
||||
|
||||
/**
|
||||
* Returns data type of the given column's value.
|
||||
*<p>
|
||||
* Returned column types are
|
||||
* <ul>
|
||||
* <li>{@link Cursor#FIELD_TYPE_NULL}</li>
|
||||
* <li>{@link Cursor#FIELD_TYPE_INTEGER}</li>
|
||||
* <li>{@link Cursor#FIELD_TYPE_FLOAT}</li>
|
||||
* <li>{@link Cursor#FIELD_TYPE_STRING}</li>
|
||||
* <li>{@link Cursor#FIELD_TYPE_BLOB}</li>
|
||||
*</ul>
|
||||
*</p>
|
||||
*
|
||||
* @param row the row to read from, row - getStartPosition() being the actual row in the window
|
||||
* @param col the column to read from
|
||||
* @return the value type
|
||||
*/
|
||||
public int getType(int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return getType_native(row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a field contains either a blob or is null.
|
||||
*
|
||||
* @param row the row to read from, row - getStartPosition() being the actual row in the window
|
||||
* @param col the column to read from
|
||||
* @return {@code true} if given field is {@code NULL} or a blob
|
||||
* @deprecated use {@link #getType(int, int)} instead
|
||||
*/
|
||||
public boolean isBlob(int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return isBlob_native(row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a field contains a long
|
||||
*
|
||||
* @param row the row to read from, row - getStartPosition() being the actual row in the window
|
||||
* @param col the column to read from
|
||||
* @return {@code true} if given field is a long
|
||||
* @deprecated use {@link #getType(int, int)} instead
|
||||
*/
|
||||
public boolean isLong(int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return isInteger_native(row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a field contains a float.
|
||||
*
|
||||
* @param row the row to read from, row - getStartPosition() being the actual row in the window
|
||||
* @param col the column to read from
|
||||
* @return {@code true} if given field is a float
|
||||
* @deprecated use {@link #getType(int, int)} instead
|
||||
*/
|
||||
public boolean isFloat(int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return isFloat_native(row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a field contains either a String or is null.
|
||||
*
|
||||
* @param row the row to read from, row - getStartPosition() being the actual row in the window
|
||||
* @param col the column to read from
|
||||
* @return {@code true} if given field is {@code NULL} or a String
|
||||
* @deprecated use {@link #getType(int, int)} instead
|
||||
*/
|
||||
public boolean isString(int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return isString_native(row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native boolean isBlob_native(int row, int col);
|
||||
private native boolean isString_native(int row, int col);
|
||||
private native boolean isInteger_native(int row, int col);
|
||||
private native boolean isFloat_native(int row, int col);
|
||||
|
||||
private native int getType_native(int row, int col);
|
||||
|
||||
/**
|
||||
* Returns a String for the given field.
|
||||
*
|
||||
* @param row the row to read from, row - getStartPosition() being the actual row in the window
|
||||
* @param col the column to read from
|
||||
* @return a String value for the given field
|
||||
*/
|
||||
public String getString(int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return getString_native(row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value at (<code>row</code>, <code>col</code>) as a <code>String</code>.
|
||||
*
|
||||
* <p>If the value is null, then <code>null</code> is returned. If the
|
||||
* type of column <code>col</code> is integral, then the result is the string
|
||||
* that is obtained by formatting the integer value with the <code>printf</code>
|
||||
* family of functions using format specifier <code>%lld</code>. If the
|
||||
* type of column <code>col</code> is floating-point, then the result is the string
|
||||
* that is obtained by formatting the floating-point value with the
|
||||
* <code>printf</code> family of functions using format specifier <code>%g</code>.
|
||||
* If the type of column <code>col</code> is a blob type, then an
|
||||
* {@link SQLiteException} is thrown.
|
||||
*/
|
||||
private native String getString_native(int row, int col);
|
||||
//private native byte[] getString_native(int row, int col);
|
||||
|
||||
/**
|
||||
* copy the text for the given field in the provided char array.
|
||||
*
|
||||
* @param row the row to read from, row - getStartPosition() being the actual row in the window
|
||||
* @param col the column to read from
|
||||
* @param buffer the CharArrayBuffer to copy the text into,
|
||||
* If the requested string is larger than the buffer
|
||||
* a new char buffer will be created to hold the string. and assigne to
|
||||
* CharArrayBuffer.data
|
||||
*/
|
||||
public void copyStringToBuffer(int row, int col, CharArrayBuffer buffer) {
|
||||
if (buffer == null) {
|
||||
throw new IllegalArgumentException("CharArrayBuffer should not be null");
|
||||
}
|
||||
if (buffer.data == null) {
|
||||
buffer.data = new char[64];
|
||||
}
|
||||
acquireReference();
|
||||
try {
|
||||
char[] newbuf = copyStringToBuffer_native(
|
||||
row - mStartPos, col, buffer.data.length, buffer);
|
||||
if (newbuf != null) {
|
||||
buffer.data = newbuf;
|
||||
}
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
private native char[] copyStringToBuffer_native(
|
||||
int row, int col, int bufferSize, CharArrayBuffer buffer);
|
||||
|
||||
/**
|
||||
* Returns a long for the given field.
|
||||
* row is 0 based
|
||||
*
|
||||
* @param row the row to read from, row - getStartPosition() being the actual row in the window
|
||||
* @param col the column to read from
|
||||
* @return a long value for the given field
|
||||
*/
|
||||
public long getLong(int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return getLong_native(row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value at (<code>row</code>, <code>col</code>) as a <code>long</code>.
|
||||
*
|
||||
* <p>If the value is null, then <code>0L</code> is returned. If the
|
||||
* type of column <code>col</code> is a string type, then the result
|
||||
* is the <code>long</code> that is obtained by parsing the string value with
|
||||
* <code>strtoll</code>. If the type of column <code>col</code> is
|
||||
* floating-point, then the result is the floating-point value casted to a <code>long</code>.
|
||||
* If the type of column <code>col</code> is a blob type, then an
|
||||
* {@link SQLiteException} is thrown.
|
||||
*/
|
||||
private native long getLong_native(int row, int col);
|
||||
|
||||
/**
|
||||
* Returns a double for the given field.
|
||||
* row is 0 based
|
||||
*
|
||||
* @param row the row to read from, row - getStartPosition() being the actual row in the window
|
||||
* @param col the column to read from
|
||||
* @return a double value for the given field
|
||||
*/
|
||||
public double getDouble(int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return getDouble_native(row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value at (<code>row</code>, <code>col</code>) as a <code>double</code>.
|
||||
*
|
||||
* <p>If the value is null, then <code>0.0</code> is returned. If the
|
||||
* type of column <code>col</code> is a string type, then the result
|
||||
* is the <code>double</code> that is obtained by parsing the string value with
|
||||
* <code>strtod</code>. If the type of column <code>col</code> is
|
||||
* integral, then the result is the integer value casted to a <code>double</code>.
|
||||
* If the type of column <code>col</code> is a blob type, then an
|
||||
* {@link SQLiteException} is thrown.
|
||||
*/
|
||||
private native double getDouble_native(int row, int col);
|
||||
|
||||
/**
|
||||
* Returns a short for the given field.
|
||||
* row is 0 based
|
||||
*
|
||||
* @param row the row to read from, row - getStartPosition() being the actual row in the window
|
||||
* @param col the column to read from
|
||||
* @return a short value for the given field
|
||||
*/
|
||||
public short getShort(int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return (short) getLong_native(row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an int for the given field.
|
||||
*
|
||||
* @param row the row to read from, row - getStartPosition() being the actual row in the window
|
||||
* @param col the column to read from
|
||||
* @return an int value for the given field
|
||||
*/
|
||||
public int getInt(int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return (int) getLong_native(row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a float for the given field.
|
||||
* row is 0 based
|
||||
*
|
||||
* @param row the row to read from, row - getStartPosition() being the actual row in the window
|
||||
* @param col the column to read from
|
||||
* @return a float value for the given field
|
||||
*/
|
||||
public float getFloat(int row, int col) {
|
||||
acquireReference();
|
||||
try {
|
||||
return (float) getDouble_native(row - mStartPos, col);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears out the existing contents of the window, making it safe to reuse
|
||||
* for new data. Note that the number of columns in the window may NOT
|
||||
* change across a call to clear().
|
||||
*/
|
||||
public void clear() {
|
||||
acquireReference();
|
||||
try {
|
||||
mStartPos = 0;
|
||||
native_clear();
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/** Clears out the native side of things */
|
||||
private native void native_clear();
|
||||
|
||||
/**
|
||||
* Cleans up the native resources associated with the window.
|
||||
*/
|
||||
public void close() {
|
||||
releaseReference();
|
||||
}
|
||||
|
||||
private native void close_native();
|
||||
|
||||
@Override
|
||||
protected void finalize() {
|
||||
// Just in case someone forgot to call close...
|
||||
if (nWindow == 0) {
|
||||
return;
|
||||
}
|
||||
close_native();
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<CursorWindow> CREATOR
|
||||
= new Parcelable.Creator<CursorWindow>() {
|
||||
public CursorWindow createFromParcel(Parcel source) {
|
||||
return new CursorWindow(source,0);
|
||||
}
|
||||
|
||||
public CursorWindow[] newArray(int size) {
|
||||
return new CursorWindow[size];
|
||||
}
|
||||
};
|
||||
|
||||
public static CursorWindow newFromParcel(Parcel p) {
|
||||
return CREATOR.createFromParcel(p);
|
||||
}
|
||||
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeStrongBinder(native_getBinder());
|
||||
dest.writeInt(mStartPos);
|
||||
}
|
||||
|
||||
public CursorWindow(Parcel source,int foo) {
|
||||
|
||||
super(true);
|
||||
|
||||
IBinder nativeBinder = source.readStrongBinder();
|
||||
mStartPos = source.readInt();
|
||||
|
||||
native_init(nativeBinder);
|
||||
}
|
||||
|
||||
/** Get the binder for the native side of the window */
|
||||
private native IBinder native_getBinder();
|
||||
|
||||
/** Does the native side initialization for an empty window */
|
||||
private native void native_init(boolean localOnly, long initialSize,
|
||||
long growthPaddingSize, long maxSize);
|
||||
|
||||
/** Does the native side initialization with an existing binder from another process */
|
||||
private native void native_init(IBinder nativeBinder);
|
||||
|
||||
@Override
|
||||
protected void onAllReferencesReleased() {
|
||||
close_native();
|
||||
|
||||
super.onAllReferencesReleased();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package net.sqlcipher;
|
||||
|
||||
public interface CursorWindowAllocation {
|
||||
long getInitialAllocationSize();
|
||||
long getGrowthPaddingSize();
|
||||
long getMaxAllocationSize();
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher;
|
||||
|
||||
/**
|
||||
* Extension of android.database.CursorWrapper to support getType() for API < 11.
|
||||
*/
|
||||
public class CursorWrapper extends android.database.CursorWrapper implements Cursor {
|
||||
|
||||
private final Cursor mCursor;
|
||||
|
||||
public CursorWrapper(Cursor cursor) {
|
||||
super(cursor);
|
||||
mCursor = cursor;
|
||||
}
|
||||
|
||||
public int getType(int columnIndex) {
|
||||
return mCursor.getType(columnIndex);
|
||||
}
|
||||
|
||||
public Cursor getWrappedCursor() {
|
||||
return mCursor;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package net.sqlcipher;
|
||||
|
||||
import net.sqlcipher.CursorWindowAllocation;
|
||||
|
||||
public class CustomCursorWindowAllocation implements CursorWindowAllocation {
|
||||
|
||||
private long initialAllocationSize = 0L;
|
||||
private long growthPaddingSize = 0L;
|
||||
private long maxAllocationSize = 0L;
|
||||
|
||||
public CustomCursorWindowAllocation(long initialSize,
|
||||
long growthPaddingSize,
|
||||
long maxAllocationSize){
|
||||
this.initialAllocationSize = initialSize;
|
||||
this.growthPaddingSize = growthPaddingSize;
|
||||
this.maxAllocationSize = maxAllocationSize;
|
||||
}
|
||||
|
||||
public long getInitialAllocationSize() {
|
||||
return initialAllocationSize;
|
||||
}
|
||||
|
||||
public long getGrowthPaddingSize() {
|
||||
return growthPaddingSize;
|
||||
}
|
||||
|
||||
public long getMaxAllocationSize() {
|
||||
return maxAllocationSize;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
/**
|
||||
* An interface to let the apps define the actions to take when the following errors are detected
|
||||
* database corruption
|
||||
*/
|
||||
public interface DatabaseErrorHandler {
|
||||
|
||||
/**
|
||||
* defines the method to be invoked when database corruption is detected.
|
||||
* @param dbObj the {@link SQLiteDatabase} object representing the database on which corruption
|
||||
* is detected.
|
||||
*/
|
||||
void onCorruption(SQLiteDatabase dbObj);
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,21 @@
|
|||
package net.sqlcipher;
|
||||
|
||||
import net.sqlcipher.CursorWindowAllocation;
|
||||
|
||||
public class DefaultCursorWindowAllocation implements CursorWindowAllocation {
|
||||
|
||||
private long initialAllocationSize = 1024 * 1024;
|
||||
private long WindowAllocationUnbounded = 0;
|
||||
|
||||
public long getInitialAllocationSize() {
|
||||
return initialAllocationSize;
|
||||
}
|
||||
|
||||
public long getGrowthPaddingSize() {
|
||||
return initialAllocationSize;
|
||||
}
|
||||
|
||||
public long getMaxAllocationSize() {
|
||||
return WindowAllocationUnbounded;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
import net.sqlcipher.database.SQLiteException;
|
||||
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
/**
|
||||
* Default class used to define the actions to take when the database corruption is reported
|
||||
* by sqlite.
|
||||
* <p>
|
||||
* If null is specified for DatabaeErrorHandler param in the above calls, then this class is used
|
||||
* as the default {@link DatabaseErrorHandler}.
|
||||
*/
|
||||
public final class DefaultDatabaseErrorHandler implements DatabaseErrorHandler {
|
||||
|
||||
private final String TAG = getClass().getSimpleName();
|
||||
|
||||
/**
|
||||
* defines the default method to be invoked when database corruption is detected.
|
||||
* @param dbObj the {@link SQLiteDatabase} object representing the database on which corruption
|
||||
* is detected.
|
||||
*/
|
||||
public void onCorruption(SQLiteDatabase dbObj) {
|
||||
// NOTE: Unlike the AOSP, this version does NOT attempt to delete any attached databases.
|
||||
// TBD: Are we really certain that the attached databases would really be corrupt?
|
||||
Log.e(TAG, "Corruption reported by sqlite on database, deleting: " + dbObj.getPath());
|
||||
|
||||
if (dbObj.isOpen()) {
|
||||
Log.e(TAG, "Database object for corrupted database is already open, closing");
|
||||
|
||||
try {
|
||||
dbObj.close();
|
||||
} catch (Exception e) {
|
||||
/* ignored */
|
||||
Log.e(TAG, "Exception closing Database object for corrupted database, ignored", e);
|
||||
}
|
||||
}
|
||||
|
||||
deleteDatabaseFile(dbObj.getPath());
|
||||
}
|
||||
|
||||
private void deleteDatabaseFile(String fileName) {
|
||||
if (fileName.equalsIgnoreCase(":memory:") || fileName.trim().length() == 0) {
|
||||
return;
|
||||
}
|
||||
Log.e(TAG, "deleting the database file: " + fileName);
|
||||
try {
|
||||
new File(fileName).delete();
|
||||
} catch (Exception e) {
|
||||
/* print warning and ignore exception */
|
||||
Log.w(TAG, "delete failed: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher;
|
||||
|
||||
import android.os.RemoteException;
|
||||
import android.os.IBinder;
|
||||
import android.os.IInterface;
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This interface provides a low-level way to pass bulk cursor data across
|
||||
* both process and language boundries. Application code should use the Cursor
|
||||
* interface directly.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public interface IBulkCursor extends IInterface {
|
||||
/**
|
||||
* Returns a BulkCursorWindow, which either has a reference to a shared
|
||||
* memory segment with the rows, or an array of JSON strings.
|
||||
*/
|
||||
public CursorWindow getWindow(int startPos) throws RemoteException;
|
||||
|
||||
public void onMove(int position) throws RemoteException;
|
||||
|
||||
/**
|
||||
* Returns the number of rows in the cursor.
|
||||
*
|
||||
* @return the number of rows in the cursor.
|
||||
*/
|
||||
public int count() throws RemoteException;
|
||||
|
||||
/**
|
||||
* Returns a string array holding the names of all of the columns in the
|
||||
* cursor in the order in which they were listed in the result.
|
||||
*
|
||||
* @return the names of the columns returned in this query.
|
||||
*/
|
||||
public String[] getColumnNames() throws RemoteException;
|
||||
|
||||
public boolean updateRows(Map<? extends Long, ? extends Map<String, Object>> values) throws RemoteException;
|
||||
|
||||
public boolean deleteRow(int position) throws RemoteException;
|
||||
|
||||
public void deactivate() throws RemoteException;
|
||||
|
||||
public void close() throws RemoteException;
|
||||
|
||||
public int requery(IContentObserver observer, CursorWindow window) throws RemoteException;
|
||||
|
||||
boolean getWantsAllOnMoveCalls() throws RemoteException;
|
||||
|
||||
Bundle getExtras() throws RemoteException;
|
||||
|
||||
Bundle respond(Bundle extras) throws RemoteException;
|
||||
|
||||
/* IPC constants */
|
||||
static final String descriptor = "android.content.IBulkCursor";
|
||||
|
||||
static final int GET_CURSOR_WINDOW_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
|
||||
static final int COUNT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 1;
|
||||
static final int GET_COLUMN_NAMES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 2;
|
||||
static final int UPDATE_ROWS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 3;
|
||||
static final int DELETE_ROW_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 4;
|
||||
static final int DEACTIVATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 5;
|
||||
static final int REQUERY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 6;
|
||||
static final int ON_MOVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 7;
|
||||
static final int WANTS_ON_MOVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 8;
|
||||
static final int GET_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 9;
|
||||
static final int RESPOND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 10;
|
||||
static final int CLOSE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 11;
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package net.sqlcipher;
|
||||
|
||||
/**
|
||||
* An exception that indicates there was an error accessing a specific row/column.
|
||||
*/
|
||||
public class InvalidRowColumnException extends RuntimeException
|
||||
{
|
||||
public InvalidRowColumnException() {}
|
||||
|
||||
public InvalidRowColumnException(String error)
|
||||
{
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.database.CharArrayBuffer;
|
||||
|
||||
|
||||
/**
|
||||
* A mutable cursor implementation backed by an array of {@code Object}s. Use
|
||||
* {@link #newRow()} to add rows. Automatically expands internal capacity
|
||||
* as needed.
|
||||
*/
|
||||
public class MatrixCursor extends AbstractCursor {
|
||||
|
||||
private final String[] columnNames;
|
||||
private Object[] data;
|
||||
private int rowCount = 0;
|
||||
private final int columnCount;
|
||||
|
||||
/**
|
||||
* Constructs a new cursor with the given initial capacity.
|
||||
*
|
||||
* @param columnNames names of the columns, the ordering of which
|
||||
* determines column ordering elsewhere in this cursor
|
||||
* @param initialCapacity in rows
|
||||
*/
|
||||
public MatrixCursor(String[] columnNames, int initialCapacity) {
|
||||
this.columnNames = columnNames;
|
||||
this.columnCount = columnNames.length;
|
||||
|
||||
if (initialCapacity < 1) {
|
||||
initialCapacity = 1;
|
||||
}
|
||||
|
||||
this.data = new Object[columnCount * initialCapacity];
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new cursor.
|
||||
*
|
||||
* @param columnNames names of the columns, the ordering of which
|
||||
* determines column ordering elsewhere in this cursor
|
||||
*/
|
||||
public MatrixCursor(String[] columnNames) {
|
||||
this(columnNames, 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets value at the given column for the current row.
|
||||
*/
|
||||
private Object get(int column) {
|
||||
if (column < 0 || column >= columnCount) {
|
||||
throw new CursorIndexOutOfBoundsException("Requested column: "
|
||||
+ column + ", # of columns: " + columnCount);
|
||||
}
|
||||
if (mPos < 0) {
|
||||
throw new CursorIndexOutOfBoundsException("Before first row.");
|
||||
}
|
||||
if (mPos >= rowCount) {
|
||||
throw new CursorIndexOutOfBoundsException("After last row.");
|
||||
}
|
||||
return data[mPos * columnCount + column];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new row to the end and returns a builder for that row. Not safe
|
||||
* for concurrent use.
|
||||
*
|
||||
* @return builder which can be used to set the column values for the new
|
||||
* row
|
||||
*/
|
||||
public RowBuilder newRow() {
|
||||
rowCount++;
|
||||
int endIndex = rowCount * columnCount;
|
||||
ensureCapacity(endIndex);
|
||||
int start = endIndex - columnCount;
|
||||
return new RowBuilder(start, endIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new row to the end with the given column values. Not safe
|
||||
* for concurrent use.
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code columnValues.length !=
|
||||
* columnNames.length}
|
||||
* @param columnValues in the same order as the the column names specified
|
||||
* at cursor construction time
|
||||
*/
|
||||
public void addRow(Object[] columnValues) {
|
||||
if (columnValues.length != columnCount) {
|
||||
throw new IllegalArgumentException("columnNames.length = "
|
||||
+ columnCount + ", columnValues.length = "
|
||||
+ columnValues.length);
|
||||
}
|
||||
|
||||
int start = rowCount++ * columnCount;
|
||||
ensureCapacity(start + columnCount);
|
||||
System.arraycopy(columnValues, 0, data, start, columnCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new row to the end with the given column values. Not safe
|
||||
* for concurrent use.
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code columnValues.size() !=
|
||||
* columnNames.length}
|
||||
* @param columnValues in the same order as the the column names specified
|
||||
* at cursor construction time
|
||||
*/
|
||||
public void addRow(Iterable<?> columnValues) {
|
||||
int start = rowCount * columnCount;
|
||||
int end = start + columnCount;
|
||||
ensureCapacity(end);
|
||||
|
||||
if (columnValues instanceof ArrayList<?>) {
|
||||
addRow((ArrayList<?>) columnValues, start);
|
||||
return;
|
||||
}
|
||||
|
||||
int current = start;
|
||||
Object[] localData = data;
|
||||
for (Object columnValue : columnValues) {
|
||||
if (current == end) {
|
||||
// TODO: null out row?
|
||||
throw new IllegalArgumentException(
|
||||
"columnValues.size() > columnNames.length");
|
||||
}
|
||||
localData[current++] = columnValue;
|
||||
}
|
||||
|
||||
if (current != end) {
|
||||
// TODO: null out row?
|
||||
throw new IllegalArgumentException(
|
||||
"columnValues.size() < columnNames.length");
|
||||
}
|
||||
|
||||
// Increase row count here in case we encounter an exception.
|
||||
rowCount++;
|
||||
}
|
||||
|
||||
/** Optimization for {@link ArrayList}. */
|
||||
private void addRow(ArrayList<?> columnValues, int start) {
|
||||
int size = columnValues.size();
|
||||
if (size != columnCount) {
|
||||
throw new IllegalArgumentException("columnNames.length = "
|
||||
+ columnCount + ", columnValues.size() = " + size);
|
||||
}
|
||||
|
||||
rowCount++;
|
||||
Object[] localData = data;
|
||||
for (int i = 0; i < size; i++) {
|
||||
localData[start + i] = columnValues.get(i);
|
||||
}
|
||||
}
|
||||
|
||||
/** Ensures that this cursor has enough capacity. */
|
||||
private void ensureCapacity(int size) {
|
||||
if (size > data.length) {
|
||||
Object[] oldData = this.data;
|
||||
int newSize = data.length * 2;
|
||||
if (newSize < size) {
|
||||
newSize = size;
|
||||
}
|
||||
this.data = new Object[newSize];
|
||||
System.arraycopy(oldData, 0, this.data, 0, oldData.length);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a row, starting from the left-most column and adding one column
|
||||
* value at a time. Follows the same ordering as the column names specified
|
||||
* at cursor construction time.
|
||||
*/
|
||||
public class RowBuilder {
|
||||
|
||||
private int index;
|
||||
private final int endIndex;
|
||||
|
||||
RowBuilder(int index, int endIndex) {
|
||||
this.index = index;
|
||||
this.endIndex = endIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the next column value in this row.
|
||||
*
|
||||
* @throws CursorIndexOutOfBoundsException if you try to add too many
|
||||
* values
|
||||
* @return this builder to support chaining
|
||||
*/
|
||||
public RowBuilder add(Object columnValue) {
|
||||
if (index == endIndex) {
|
||||
throw new CursorIndexOutOfBoundsException(
|
||||
"No more columns left.");
|
||||
}
|
||||
|
||||
data[index++] = columnValue;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
// AbstractCursor implementation.
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return rowCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getColumnNames() {
|
||||
return columnNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(int column) {
|
||||
Object value = get(column);
|
||||
if (value == null) return null;
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getShort(int column) {
|
||||
Object value = get(column);
|
||||
if (value == null) return 0;
|
||||
if (value instanceof Number) return ((Number) value).shortValue();
|
||||
return Short.parseShort(value.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(int column) {
|
||||
Object value = get(column);
|
||||
if (value == null) return 0;
|
||||
if (value instanceof Number) return ((Number) value).intValue();
|
||||
return Integer.parseInt(value.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(int column) {
|
||||
Object value = get(column);
|
||||
if (value == null) return 0;
|
||||
if (value instanceof Number) return ((Number) value).longValue();
|
||||
return Long.parseLong(value.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFloat(int column) {
|
||||
Object value = get(column);
|
||||
if (value == null) return 0.0f;
|
||||
if (value instanceof Number) return ((Number) value).floatValue();
|
||||
return Float.parseFloat(value.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(int column) {
|
||||
Object value = get(column);
|
||||
if (value == null) return 0.0d;
|
||||
if (value instanceof Number) return ((Number) value).doubleValue();
|
||||
return Double.parseDouble(value.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getType(int column) {
|
||||
return DatabaseUtils.getTypeOfObject(get(column));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNull(int column) {
|
||||
return get(column) == null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package net.sqlcipher;
|
||||
|
||||
/**
|
||||
* An exception that indicates there was an error attempting to allocate a row
|
||||
* for the CursorWindow.
|
||||
*/
|
||||
public class RowAllocationException extends RuntimeException
|
||||
{
|
||||
public RowAllocationException() {}
|
||||
|
||||
public RowAllocationException(String error)
|
||||
{
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher;
|
||||
|
||||
/**
|
||||
* An exception that indicates there was an error with SQL parsing or execution.
|
||||
*/
|
||||
public class SQLException extends RuntimeException
|
||||
{
|
||||
public SQLException() {}
|
||||
|
||||
public SQLException(String error)
|
||||
{
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher;
|
||||
|
||||
/**
|
||||
* This exception is thrown when a Cursor contains stale data and must be
|
||||
* requeried before being used again.
|
||||
*/
|
||||
public class StaleDataException extends java.lang.RuntimeException
|
||||
{
|
||||
public StaleDataException()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public StaleDataException(String description)
|
||||
{
|
||||
super(description);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package net.sqlcipher;
|
||||
|
||||
/**
|
||||
* An exception that indicates an unknown type was returned.
|
||||
*/
|
||||
public class UnknownTypeException extends RuntimeException
|
||||
{
|
||||
public UnknownTypeException() {}
|
||||
|
||||
public UnknownTypeException(String error)
|
||||
{
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright (C) 2019 Mark L. Murphy
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
|
||||
import android.util.SparseArray;
|
||||
import androidx.sqlite.db.SupportSQLiteProgram;
|
||||
|
||||
/*
|
||||
This class is a concrete implementation of SupportSQLiteProgram,
|
||||
for use in supporting bind arguments for the SQLiteDatabase query()
|
||||
methods that take a SupportSQLiteQuery parameter.
|
||||
|
||||
In Google's FrameworkSQLiteDatabase implementation of those query()
|
||||
methods, it uses FrameworkSQLiteProgram, which has the same basic implementation
|
||||
as does BindingsRecorder.
|
||||
*/
|
||||
class BindingsRecorder implements SupportSQLiteProgram {
|
||||
private SparseArray<Object> bindings=new SparseArray<>();
|
||||
|
||||
@Override
|
||||
public void bindNull(int index) {
|
||||
bindings.put(index, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindLong(int index, long value) {
|
||||
bindings.put(index, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindDouble(int index, double value) {
|
||||
bindings.put(index, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindString(int index, String value) {
|
||||
bindings.put(index, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindBlob(int index, byte[] value) {
|
||||
bindings.put(index, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearBindings() {
|
||||
bindings.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
clearBindings();
|
||||
}
|
||||
|
||||
String[] getBindings() {
|
||||
final String[] result=new String[bindings.size()];
|
||||
|
||||
for (int i=0;i<bindings.size();i++) {
|
||||
int key=bindings.keyAt(i);
|
||||
Object binding=bindings.get(key);
|
||||
|
||||
if (binding!=null) {
|
||||
result[i]=bindings.get(key).toString();
|
||||
}
|
||||
else {
|
||||
result[i]=""; // SQLCipher does not like null binding values
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
|
||||
/**
|
||||
* An exception that indicates that garbage-collector is finalizing a database object
|
||||
* that is not explicitly closed
|
||||
* @hide
|
||||
*/
|
||||
public class DatabaseObjectNotClosedException extends RuntimeException
|
||||
{
|
||||
private static final String s = "Application did not close the cursor or database object " +
|
||||
"that was opened here";
|
||||
|
||||
public DatabaseObjectNotClosedException()
|
||||
{
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
|
||||
/**
|
||||
* An exception that indicates that the SQLite program was aborted.
|
||||
* This can happen either through a call to ABORT in a trigger,
|
||||
* or as the result of using the ABORT conflict clause.
|
||||
*/
|
||||
public class SQLiteAbortException extends SQLiteException {
|
||||
public SQLiteAbortException() {}
|
||||
|
||||
public SQLiteAbortException(String error) {
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
|
||||
import net.sqlcipher.*;
|
||||
|
||||
/**
|
||||
* An object created from a SQLiteDatabase that can be closed.
|
||||
*/
|
||||
public abstract class SQLiteClosable {
|
||||
private int mReferenceCount = 1;
|
||||
private Object mLock = new Object();
|
||||
|
||||
protected abstract void onAllReferencesReleased();
|
||||
protected void onAllReferencesReleasedFromContainer() {}
|
||||
|
||||
public void acquireReference() {
|
||||
synchronized(mLock) {
|
||||
if (mReferenceCount <= 0) {
|
||||
throw new IllegalStateException(
|
||||
"attempt to re-open an already-closed object: " + getObjInfo());
|
||||
}
|
||||
mReferenceCount++;
|
||||
}
|
||||
}
|
||||
|
||||
public void releaseReference() {
|
||||
synchronized(mLock) {
|
||||
mReferenceCount--;
|
||||
if (mReferenceCount == 0) {
|
||||
onAllReferencesReleased();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void releaseReferenceFromContainer() {
|
||||
synchronized(mLock) {
|
||||
mReferenceCount--;
|
||||
if (mReferenceCount == 0) {
|
||||
onAllReferencesReleasedFromContainer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getObjInfo() {
|
||||
StringBuilder buff = new StringBuilder();
|
||||
buff.append(this.getClass().getName());
|
||||
buff.append(" (");
|
||||
if (this instanceof SQLiteDatabase) {
|
||||
buff.append("database = ");
|
||||
buff.append(((SQLiteDatabase)this).getPath());
|
||||
} else if (this instanceof SQLiteProgram || this instanceof SQLiteStatement ||
|
||||
this instanceof SQLiteQuery) {
|
||||
buff.append("mSql = ");
|
||||
buff.append(((SQLiteProgram)this).mSql);
|
||||
}
|
||||
/*else if (this instanceof CursorWindow) {
|
||||
buff.append("mStartPos = ");
|
||||
buff.append(((CursorWindow)this).getStartPosition());
|
||||
}*/
|
||||
buff.append(") ");
|
||||
return buff.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* This class encapsulates compilation of sql statement and release of the compiled statement obj.
|
||||
* Once a sql statement is compiled, it is cached in {@link SQLiteDatabase}
|
||||
* and it is released in one of the 2 following ways
|
||||
* 1. when {@link SQLiteDatabase} object is closed.
|
||||
* 2. if this is not cached in {@link SQLiteDatabase}, {@link android.database.Cursor#close()}
|
||||
* releaases this obj.
|
||||
*/
|
||||
/* package */ class SQLiteCompiledSql {
|
||||
|
||||
private static final String TAG = "SQLiteCompiledSql";
|
||||
|
||||
/** The database this program is compiled against. */
|
||||
/* package */ SQLiteDatabase mDatabase;
|
||||
|
||||
/**
|
||||
* Native linkage, do not modify. This comes from the database.
|
||||
*/
|
||||
/* package */ long nHandle = 0;
|
||||
|
||||
/**
|
||||
* Native linkage, do not modify. When non-0 this holds a reference to a valid
|
||||
* sqlite3_statement object. It is only updated by the native code, but may be
|
||||
* checked in this class when the database lock is held to determine if there
|
||||
* is a valid native-side program or not.
|
||||
*/
|
||||
/* package */ long nStatement = 0;
|
||||
|
||||
/** the following are for debugging purposes */
|
||||
private String mSqlStmt = null;
|
||||
private Throwable mStackTrace = null;
|
||||
|
||||
/** when in cache and is in use, this member is set */
|
||||
private boolean mInUse = false;
|
||||
|
||||
/* package */ SQLiteCompiledSql(SQLiteDatabase db, String sql) {
|
||||
if (!db.isOpen()) {
|
||||
throw new IllegalStateException("database " + db.getPath() + " already closed");
|
||||
}
|
||||
mDatabase = db;
|
||||
mSqlStmt = sql;
|
||||
mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace();
|
||||
this.nHandle = db.mNativeHandle;
|
||||
compile(sql, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the given SQL into a SQLite byte code program using sqlite3_prepare_v2(). If
|
||||
* this method has been called previously without a call to close and forCompilation is set
|
||||
* to false the previous compilation will be used. Setting forceCompilation to true will
|
||||
* always re-compile the program and should be done if you pass differing SQL strings to this
|
||||
* method.
|
||||
*
|
||||
* <P>Note: this method acquires the database lock.</P>
|
||||
*
|
||||
* @param sql the SQL string to compile
|
||||
* @param forceCompilation forces the SQL to be recompiled in the event that there is an
|
||||
* existing compiled SQL program already around
|
||||
*/
|
||||
private void compile(String sql, boolean forceCompilation) {
|
||||
if (!mDatabase.isOpen()) {
|
||||
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
|
||||
}
|
||||
// Only compile if we don't have a valid statement already or the caller has
|
||||
// explicitly requested a recompile.
|
||||
if (forceCompilation) {
|
||||
mDatabase.lock();
|
||||
try {
|
||||
// Note that the native_compile() takes care of destroying any previously
|
||||
// existing programs before it compiles.
|
||||
native_compile(sql);
|
||||
} finally {
|
||||
mDatabase.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ void releaseSqlStatement() {
|
||||
// Note that native_finalize() checks to make sure that nStatement is
|
||||
// non-null before destroying it.
|
||||
if (nStatement != 0) {
|
||||
if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
|
||||
Log.v(TAG, "closed and deallocated DbObj (id#" + nStatement +")");
|
||||
}
|
||||
try {
|
||||
mDatabase.lock();
|
||||
native_finalize();
|
||||
nStatement = 0;
|
||||
} finally {
|
||||
mDatabase.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if acquire() succeeds. false otherwise.
|
||||
*/
|
||||
/* package */ synchronized boolean acquire() {
|
||||
if (mInUse) {
|
||||
// someone already has acquired it.
|
||||
return false;
|
||||
}
|
||||
mInUse = true;
|
||||
if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
|
||||
Log.v(TAG, "Acquired DbObj (id#" + nStatement + ") from DB cache");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* package */ synchronized void release() {
|
||||
if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
|
||||
Log.v(TAG, "Released DbObj (id#" + nStatement + ") back to DB cache");
|
||||
}
|
||||
mInUse = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that the native resource is cleaned up.
|
||||
*/
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
try {
|
||||
if (nStatement == 0) return;
|
||||
// finalizer should NEVER get called
|
||||
if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
|
||||
Log.v(TAG, "** warning ** Finalized DbObj (id#" + nStatement + ")");
|
||||
}
|
||||
int len = mSqlStmt.length();
|
||||
Log.w(TAG, "Releasing statement in a finalizer. Please ensure " +
|
||||
"that you explicitly call close() on your cursor: " +
|
||||
mSqlStmt.substring(0, (len > 100) ? 100 : len), mStackTrace);
|
||||
releaseSqlStatement();
|
||||
} finally {
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles SQL into a SQLite program.
|
||||
*
|
||||
* <P>The database lock must be held when calling this method.
|
||||
* @param sql The SQL to compile.
|
||||
*/
|
||||
private final native void native_compile(String sql);
|
||||
private final native void native_finalize();
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
|
||||
/**
|
||||
* An exception that indicates that an integrity constraint was violated.
|
||||
*/
|
||||
public class SQLiteConstraintException extends SQLiteException {
|
||||
public SQLiteConstraintException() {}
|
||||
|
||||
public SQLiteConstraintException(String error) {
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
import net.sqlcipher.*;
|
||||
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.os.MemoryFile;
|
||||
|
||||
import android.database.Cursor;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Some helper functions for using SQLite database to implement content providers.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class SQLiteContentHelper {
|
||||
|
||||
/**
|
||||
* Runs an SQLite query and returns an AssetFileDescriptor for the
|
||||
* blob in column 0 of the first row. If the first column does
|
||||
* not contain a blob, an unspecified exception is thrown.
|
||||
*
|
||||
* @param db Handle to a readable database.
|
||||
* @param sql SQL query, possibly with query arguments.
|
||||
* @param selectionArgs Query argument values, or {@code null} for no argument.
|
||||
* @return If no exception is thrown, a non-null AssetFileDescriptor is returned.
|
||||
* @throws FileNotFoundException If the query returns no results or the
|
||||
* value of column 0 is NULL, or if there is an error creating the
|
||||
* asset file descriptor.
|
||||
*/
|
||||
public static AssetFileDescriptor getBlobColumnAsAssetFile(SQLiteDatabase db, String sql,
|
||||
String[] selectionArgs) throws FileNotFoundException {
|
||||
android.os.ParcelFileDescriptor fd = null;
|
||||
|
||||
try {
|
||||
MemoryFile file = simpleQueryForBlobMemoryFile(db, sql, selectionArgs);
|
||||
if (file == null) {
|
||||
throw new FileNotFoundException("No results.");
|
||||
}
|
||||
Class c = file.getClass();
|
||||
try {
|
||||
java.lang.reflect.Method m = c.getDeclaredMethod("getParcelFileDescriptor");
|
||||
m.setAccessible(true);
|
||||
fd = (android.os.ParcelFileDescriptor)m.invoke(file);
|
||||
} catch (Exception e) {
|
||||
android.util.Log.i("SQLiteContentHelper", "SQLiteCursor.java: " + e);
|
||||
}
|
||||
AssetFileDescriptor afd = new AssetFileDescriptor(fd, 0, file.length());
|
||||
return afd;
|
||||
} catch (IOException ex) {
|
||||
throw new FileNotFoundException(ex.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs an SQLite query and returns a MemoryFile for the
|
||||
* blob in column 0 of the first row. If the first column does
|
||||
* not contain a blob, an unspecified exception is thrown.
|
||||
*
|
||||
* @return A memory file, or {@code null} if the query returns no results
|
||||
* or the value column 0 is NULL.
|
||||
* @throws IOException If there is an error creating the memory file.
|
||||
*/
|
||||
// TODO: make this native and use the SQLite blob API to reduce copying
|
||||
private static MemoryFile simpleQueryForBlobMemoryFile(SQLiteDatabase db, String sql,
|
||||
String[] selectionArgs) throws IOException {
|
||||
Cursor cursor = db.rawQuery(sql, selectionArgs);
|
||||
if (cursor == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
if (!cursor.moveToFirst()) {
|
||||
return null;
|
||||
}
|
||||
byte[] bytes = cursor.getBlob(0);
|
||||
if (bytes == null) {
|
||||
return null;
|
||||
}
|
||||
MemoryFile file = new MemoryFile(null, bytes.length);
|
||||
file.writeBytes(bytes, 0, 0, bytes.length);
|
||||
|
||||
// file.deactivate();
|
||||
return file;
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,700 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
|
||||
import net.sqlcipher.AbstractWindowedCursor;
|
||||
import net.sqlcipher.BuildConfig;
|
||||
import net.sqlcipher.CursorWindow;
|
||||
import net.sqlcipher.SQLException;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import android.database.CharArrayBuffer;
|
||||
import android.database.DataSetObserver;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Process;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Config;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* A Cursor implementation that exposes results from a query on a
|
||||
* {@link SQLiteDatabase}.
|
||||
*
|
||||
* SQLiteCursor is not internally synchronized so code using a SQLiteCursor from multiple
|
||||
* threads should perform its own synchronization when using the SQLiteCursor.
|
||||
*/
|
||||
public class SQLiteCursor extends AbstractWindowedCursor {
|
||||
static final String TAG = "Cursor";
|
||||
static final int NO_COUNT = -1;
|
||||
|
||||
/** The name of the table to edit */
|
||||
private String mEditTable;
|
||||
|
||||
/** The names of the columns in the rows */
|
||||
private String[] mColumns;
|
||||
|
||||
/** The query object for the cursor */
|
||||
private SQLiteQuery mQuery;
|
||||
|
||||
/** The database the cursor was created from */
|
||||
private SQLiteDatabase mDatabase;
|
||||
|
||||
/** The compiled query this cursor came from */
|
||||
private SQLiteCursorDriver mDriver;
|
||||
|
||||
/** The number of rows in the cursor */
|
||||
private int mCount = NO_COUNT;
|
||||
|
||||
private int mCursorWindowCapacity = 0;
|
||||
|
||||
private boolean fillWindowForwardOnly = false;
|
||||
|
||||
/** A mapping of column names to column indices, to speed up lookups */
|
||||
private Map<String, Integer> mColumnNameMap;
|
||||
|
||||
/** Used to find out where a cursor was allocated in case it never got released. */
|
||||
private Throwable mStackTrace;
|
||||
|
||||
/**
|
||||
* mMaxRead is the max items that each cursor window reads
|
||||
* default to a very high value
|
||||
*/
|
||||
private int mMaxRead = Integer.MAX_VALUE;
|
||||
private int mInitialRead = Integer.MAX_VALUE;
|
||||
private int mCursorState = 0;
|
||||
private ReentrantLock mLock = null;
|
||||
private boolean mPendingData = false;
|
||||
|
||||
public void setFillWindowForwardOnly(boolean value) {
|
||||
fillWindowForwardOnly = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* support for a cursor variant that doesn't always read all results
|
||||
* initialRead is the initial number of items that cursor window reads
|
||||
* if query contains more than this number of items, a thread will be
|
||||
* created and handle the left over items so that caller can show
|
||||
* results as soon as possible
|
||||
* @param initialRead initial number of items that cursor read
|
||||
* @param maxRead leftover items read at maxRead items per time
|
||||
* @hide
|
||||
*/
|
||||
public void setLoadStyle(int initialRead, int maxRead) {
|
||||
mMaxRead = maxRead;
|
||||
mInitialRead = initialRead;
|
||||
mLock = new ReentrantLock(true);
|
||||
}
|
||||
|
||||
private void queryThreadLock() {
|
||||
if (mLock != null) {
|
||||
mLock.lock();
|
||||
}
|
||||
}
|
||||
|
||||
private void queryThreadUnlock() {
|
||||
if (mLock != null) {
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
final private class QueryThread implements Runnable {
|
||||
private final int mThreadState;
|
||||
QueryThread(int version) {
|
||||
mThreadState = version;
|
||||
}
|
||||
private void sendMessage() {
|
||||
if (mNotificationHandler != null) {
|
||||
mNotificationHandler.sendEmptyMessage(1);
|
||||
mPendingData = false;
|
||||
} else {
|
||||
mPendingData = true;
|
||||
}
|
||||
|
||||
}
|
||||
public void run() {
|
||||
// use cached mWindow, to avoid get null mWindow
|
||||
CursorWindow cw = mWindow;
|
||||
Process.setThreadPriority(Process.myTid(), Process.THREAD_PRIORITY_BACKGROUND);
|
||||
// the cursor's state doesn't change
|
||||
while (true) {
|
||||
if(mLock == null){
|
||||
mLock = new ReentrantLock(true);
|
||||
}
|
||||
mLock.lock();
|
||||
if (mCursorState != mThreadState) {
|
||||
mLock.unlock();
|
||||
break;
|
||||
}
|
||||
try {
|
||||
int count = mQuery.fillWindow(cw, mMaxRead, mCount);
|
||||
// return -1 means not finished
|
||||
if (count != 0) {
|
||||
if (count == NO_COUNT){
|
||||
mCount += mMaxRead;
|
||||
sendMessage();
|
||||
} else {
|
||||
mCount = count;
|
||||
sendMessage();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// end the tread when the cursor is close
|
||||
break;
|
||||
} finally {
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
protected static class MainThreadNotificationHandler extends Handler {
|
||||
|
||||
private final WeakReference<SQLiteCursor> wrappedCursor;
|
||||
|
||||
MainThreadNotificationHandler(SQLiteCursor cursor) {
|
||||
wrappedCursor = new WeakReference<SQLiteCursor>(cursor);
|
||||
}
|
||||
|
||||
public void handleMessage(Message msg) {
|
||||
SQLiteCursor cursor = wrappedCursor.get();
|
||||
if(cursor != null){
|
||||
cursor.notifyDataSetChange();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
protected MainThreadNotificationHandler mNotificationHandler;
|
||||
|
||||
public void registerDataSetObserver(DataSetObserver observer) {
|
||||
super.registerDataSetObserver(observer);
|
||||
if ((Integer.MAX_VALUE != mMaxRead || Integer.MAX_VALUE != mInitialRead) &&
|
||||
mNotificationHandler == null) {
|
||||
queryThreadLock();
|
||||
try {
|
||||
mNotificationHandler = new MainThreadNotificationHandler(this);
|
||||
if (mPendingData) {
|
||||
notifyDataSetChange();
|
||||
mPendingData = false;
|
||||
}
|
||||
} finally {
|
||||
queryThreadUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a query and provide access to its result set through a Cursor
|
||||
* interface. For a query such as: {@code SELECT name, birth, phone FROM
|
||||
* myTable WHERE ... LIMIT 1,20 ORDER BY...} the column names (name, birth,
|
||||
* phone) would be in the projection argument and everything from
|
||||
* {@code FROM} onward would be in the params argument. This constructor
|
||||
* has package scope.
|
||||
*
|
||||
* @param db a reference to a Database object that is already constructed
|
||||
* and opened
|
||||
* @param editTable the name of the table used for this query
|
||||
* @param query the rest of the query terms
|
||||
* cursor is finalized
|
||||
*/
|
||||
public SQLiteCursor(SQLiteDatabase db, SQLiteCursorDriver driver,
|
||||
String editTable, SQLiteQuery query) {
|
||||
// The AbstractCursor constructor needs to do some setup.
|
||||
super();
|
||||
mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace();
|
||||
mDatabase = db;
|
||||
mDriver = driver;
|
||||
mEditTable = editTable;
|
||||
mColumnNameMap = null;
|
||||
mQuery = query;
|
||||
|
||||
try {
|
||||
db.lock();
|
||||
|
||||
// Setup the list of columns
|
||||
int columnCount = mQuery.columnCountLocked();
|
||||
mColumns = new String[columnCount];
|
||||
|
||||
// Read in all column names
|
||||
for (int i = 0; i < columnCount; i++) {
|
||||
String columnName = mQuery.columnNameLocked(i);
|
||||
mColumns[i] = columnName;
|
||||
if(BuildConfig.DEBUG){
|
||||
Log.v("DatabaseWindow", "mColumns[" + i + "] is "
|
||||
+ mColumns[i]);
|
||||
}
|
||||
|
||||
// Make note of the row ID column index for quick access to it
|
||||
if ("_id".equals(columnName)) {
|
||||
mRowIdColumnIndex = i;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
db.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the SQLiteDatabase that this cursor is associated with.
|
||||
*/
|
||||
public SQLiteDatabase getDatabase() {
|
||||
return mDatabase;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMove(int oldPosition, int newPosition) {
|
||||
// Make sure the row at newPosition is present in the window
|
||||
if (mWindow == null || newPosition < mWindow.getStartPosition() ||
|
||||
newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) {
|
||||
fillWindow(newPosition);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
if (mCount == NO_COUNT) {
|
||||
fillWindow(0);
|
||||
}
|
||||
return mCount;
|
||||
}
|
||||
|
||||
private void fillWindow (int requiredPos) {
|
||||
int startPos = 0;
|
||||
if (mWindow == null) {
|
||||
// If there isn't a window set already it will only be accessed locally
|
||||
mWindow = new CursorWindow(true /* the window is local only */);
|
||||
} else {
|
||||
mCursorState++;
|
||||
queryThreadLock();
|
||||
try {
|
||||
mWindow.clear();
|
||||
} finally {
|
||||
queryThreadUnlock();
|
||||
}
|
||||
}
|
||||
if(fillWindowForwardOnly) {
|
||||
startPos = requiredPos;
|
||||
} else {
|
||||
startPos = mCount == NO_COUNT
|
||||
? cursorPickFillWindowStartPosition(requiredPos, 0)
|
||||
: cursorPickFillWindowStartPosition(requiredPos, mCursorWindowCapacity);
|
||||
}
|
||||
mWindow.setStartPosition(startPos);
|
||||
mWindow.setRequiredPosition(requiredPos);
|
||||
if(BuildConfig.DEBUG){
|
||||
Log.v(TAG, String.format("Filling cursor window with start position:%d required position:%d",
|
||||
startPos, requiredPos));
|
||||
}
|
||||
mCount = mQuery.fillWindow(mWindow, mInitialRead, 0);
|
||||
if(mCursorWindowCapacity == 0) {
|
||||
mCursorWindowCapacity = mWindow.getNumRows();
|
||||
}
|
||||
// return -1 means not finished
|
||||
if (mCount == NO_COUNT){
|
||||
mCount = startPos + mInitialRead;
|
||||
Thread t = new Thread(new QueryThread(mCursorState), "query thread");
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnIndex(String columnName) {
|
||||
// Create mColumnNameMap on demand
|
||||
if (mColumnNameMap == null) {
|
||||
String[] columns = mColumns;
|
||||
int columnCount = columns.length;
|
||||
HashMap<String, Integer> map = new HashMap<String, Integer>(columnCount, 1);
|
||||
for (int i = 0; i < columnCount; i++) {
|
||||
map.put(columns[i], i);
|
||||
}
|
||||
mColumnNameMap = map;
|
||||
}
|
||||
|
||||
// Hack according to bug 903852
|
||||
final int periodIndex = columnName.lastIndexOf('.');
|
||||
if (periodIndex != -1) {
|
||||
Exception e = new Exception();
|
||||
if(BuildConfig.DEBUG){
|
||||
Log.e(TAG, "requesting column name with table name -- " + columnName, e);
|
||||
columnName = columnName.substring(periodIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
Integer i = mColumnNameMap.get(columnName);
|
||||
if (i != null) {
|
||||
return i.intValue();
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
// @Override
|
||||
public boolean deleteRow() {
|
||||
checkPosition();
|
||||
|
||||
// Only allow deletes if there is an ID column, and the ID has been read from it
|
||||
if (mRowIdColumnIndex == -1 || mCurrentRowID == null) {
|
||||
if(BuildConfig.DEBUG){
|
||||
Log.e(TAG,
|
||||
"Could not delete row because either the row ID column is not available or it" +
|
||||
"has not been read.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean success;
|
||||
|
||||
/*
|
||||
* Ensure we don't change the state of the database when another
|
||||
* thread is holding the database lock. requery() and moveTo() are also
|
||||
* synchronized here to make sure they get the state of the database
|
||||
* immediately following the DELETE.
|
||||
*/
|
||||
mDatabase.lock();
|
||||
try {
|
||||
try {
|
||||
mDatabase.delete(mEditTable, mColumns[mRowIdColumnIndex] + "=?",
|
||||
new String[] {mCurrentRowID.toString()});
|
||||
success = true;
|
||||
} catch (SQLException e) {
|
||||
success = false;
|
||||
}
|
||||
|
||||
int pos = mPos;
|
||||
requery();
|
||||
|
||||
/*
|
||||
* Ensure proper cursor state. Note that mCurrentRowID changes
|
||||
* in this call.
|
||||
*/
|
||||
moveToPosition(pos);
|
||||
} finally {
|
||||
mDatabase.unlock();
|
||||
}
|
||||
|
||||
if (success) {
|
||||
onChange(true);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getColumnNames() {
|
||||
return mColumns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
// @Override
|
||||
public boolean supportsUpdates() {
|
||||
// return super.supportsUpdates() && !TextUtils.isEmpty(mEditTable);
|
||||
return !TextUtils.isEmpty(mEditTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @deprecated
|
||||
*/
|
||||
// @Override
|
||||
public boolean commitUpdates(Map<? extends Long,
|
||||
? extends Map<String, Object>> additionalValues) {
|
||||
if (!supportsUpdates()) {
|
||||
if(BuildConfig.DEBUG){
|
||||
Log.e(TAG, "commitUpdates not supported on this cursor, did you "
|
||||
+ "include the _id column?");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prevent other threads from changing the updated rows while they're
|
||||
* being processed here.
|
||||
*/
|
||||
synchronized (mUpdatedRows) {
|
||||
if (additionalValues != null) {
|
||||
mUpdatedRows.putAll(additionalValues);
|
||||
}
|
||||
|
||||
if (mUpdatedRows.size() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prevent other threads from changing the database state while
|
||||
* we process the updated rows, and prevents us from changing the
|
||||
* database behind the back of another thread.
|
||||
*/
|
||||
mDatabase.beginTransaction();
|
||||
try {
|
||||
StringBuilder sql = new StringBuilder(128);
|
||||
|
||||
// For each row that has been updated
|
||||
for (Map.Entry<Long, Map<String, Object>> rowEntry :
|
||||
mUpdatedRows.entrySet()) {
|
||||
Map<String, Object> values = rowEntry.getValue();
|
||||
Long rowIdObj = rowEntry.getKey();
|
||||
|
||||
if (rowIdObj == null || values == null) {
|
||||
throw new IllegalStateException("null rowId or values found! rowId = "
|
||||
+ rowIdObj + ", values = " + values);
|
||||
}
|
||||
|
||||
if (values.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
long rowId = rowIdObj.longValue();
|
||||
|
||||
Iterator<Map.Entry<String, Object>> valuesIter =
|
||||
values.entrySet().iterator();
|
||||
|
||||
sql.setLength(0);
|
||||
sql.append("UPDATE " + mEditTable + " SET ");
|
||||
|
||||
// For each column value that has been updated
|
||||
Object[] bindings = new Object[values.size()];
|
||||
int i = 0;
|
||||
while (valuesIter.hasNext()) {
|
||||
Map.Entry<String, Object> entry = valuesIter.next();
|
||||
sql.append(entry.getKey());
|
||||
sql.append("=?");
|
||||
bindings[i] = entry.getValue();
|
||||
if (valuesIter.hasNext()) {
|
||||
sql.append(", ");
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
sql.append(" WHERE " + mColumns[mRowIdColumnIndex]
|
||||
+ '=' + rowId);
|
||||
sql.append(';');
|
||||
mDatabase.execSQL(sql.toString(), bindings);
|
||||
mDatabase.rowUpdated(mEditTable, rowId);
|
||||
}
|
||||
mDatabase.setTransactionSuccessful();
|
||||
} finally {
|
||||
mDatabase.endTransaction();
|
||||
}
|
||||
|
||||
mUpdatedRows.clear();
|
||||
}
|
||||
|
||||
// Let any change observers know about the update
|
||||
onChange(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void deactivateCommon() {
|
||||
if(BuildConfig.DEBUG) Log.v(TAG, "<<< Releasing cursor " + this);
|
||||
mCursorState = 0;
|
||||
if (mWindow != null) {
|
||||
mWindow.close();
|
||||
mWindow = null;
|
||||
}
|
||||
if(BuildConfig.DEBUG) Log.v("DatabaseWindow", "closing window in release()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
super.deactivate();
|
||||
deactivateCommon();
|
||||
mDriver.cursorDeactivated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
deactivateCommon();
|
||||
mQuery.close();
|
||||
mDriver.cursorClosed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requery() {
|
||||
if (isClosed()) {
|
||||
return false;
|
||||
}
|
||||
long timeStart = 0;
|
||||
if (Config.LOGV) {
|
||||
timeStart = System.currentTimeMillis();
|
||||
}
|
||||
/*
|
||||
* Synchronize on the database lock to ensure that mCount matches the
|
||||
* results of mQuery.requery().
|
||||
*/
|
||||
mDatabase.lock();
|
||||
try {
|
||||
if (mWindow != null) {
|
||||
mWindow.clear();
|
||||
}
|
||||
mPos = -1;
|
||||
// This one will recreate the temp table, and get its count
|
||||
mDriver.cursorRequeried(this);
|
||||
mCount = NO_COUNT;
|
||||
mCursorState++;
|
||||
queryThreadLock();
|
||||
try {
|
||||
mQuery.requery();
|
||||
} finally {
|
||||
queryThreadUnlock();
|
||||
}
|
||||
} finally {
|
||||
mDatabase.unlock();
|
||||
}
|
||||
|
||||
if(BuildConfig.DEBUG){
|
||||
Log.v("DatabaseWindow", "closing window in requery()");
|
||||
Log.v(TAG, "--- Requery()ed cursor " + this + ": " + mQuery);
|
||||
}
|
||||
|
||||
boolean result = super.requery();
|
||||
if(BuildConfig.DEBUG){
|
||||
long timeEnd = System.currentTimeMillis();
|
||||
Log.v(TAG, "requery (" + (timeEnd - timeStart) + " ms): " + mDriver.toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWindow(CursorWindow window) {
|
||||
if (mWindow != null) {
|
||||
mCursorState++;
|
||||
queryThreadLock();
|
||||
try {
|
||||
mWindow.close();
|
||||
} finally {
|
||||
queryThreadUnlock();
|
||||
}
|
||||
mCount = NO_COUNT;
|
||||
}
|
||||
mWindow = window;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the selection arguments. The new values take effect after a call to requery().
|
||||
*/
|
||||
public void setSelectionArguments(String[] selectionArgs) {
|
||||
mDriver.setBindArguments(selectionArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the native resources, if they haven't been released yet.
|
||||
*/
|
||||
@Override
|
||||
protected void finalize() {
|
||||
try {
|
||||
// if the cursor hasn't been closed yet, close it first
|
||||
if (mWindow != null) {
|
||||
int len = mQuery.mSql.length();
|
||||
if(BuildConfig.DEBUG){
|
||||
Log.e(TAG, "Finalizing a Cursor that has not been deactivated or closed. " +
|
||||
"database = " + mDatabase.getPath() + ", table = " + mEditTable +
|
||||
", query = " + mQuery.mSql.substring(0, (len > 100) ? 100 : len),
|
||||
mStackTrace);
|
||||
}
|
||||
close();
|
||||
SQLiteDebug.notifyActiveCursorFinalized();
|
||||
} else {
|
||||
if(BuildConfig.DEBUG) {
|
||||
Log.v(TAG, "Finalizing cursor on database = " + mDatabase.getPath() +
|
||||
", table = " + mEditTable + ", query = " + mQuery.mSql);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void fillWindow(int requiredPos, android.database.CursorWindow window) {
|
||||
int startPos = 0;
|
||||
if (mWindow == null) {
|
||||
// If there isn't a window set already it will only be accessed locally
|
||||
mWindow = new CursorWindow(true /* the window is local only */);
|
||||
} else {
|
||||
mCursorState++;
|
||||
queryThreadLock();
|
||||
try {
|
||||
mWindow.clear();
|
||||
} finally {
|
||||
queryThreadUnlock();
|
||||
}
|
||||
}
|
||||
if(fillWindowForwardOnly) {
|
||||
startPos = requiredPos;
|
||||
} else {
|
||||
startPos = mCount == NO_COUNT
|
||||
? cursorPickFillWindowStartPosition(requiredPos, 0)
|
||||
: cursorPickFillWindowStartPosition(requiredPos, mCursorWindowCapacity);
|
||||
}
|
||||
mWindow.setStartPosition(startPos);
|
||||
mWindow.setRequiredPosition(requiredPos);
|
||||
if(BuildConfig.DEBUG) {
|
||||
Log.v(TAG, String.format("Filling cursor window with start position:%d required position:%d",
|
||||
startPos, requiredPos));
|
||||
}
|
||||
mCount = mQuery.fillWindow(mWindow, mInitialRead, 0);
|
||||
if(mCursorWindowCapacity == 0) {
|
||||
mCursorWindowCapacity = mWindow.getNumRows();
|
||||
}
|
||||
// return -1 means not finished
|
||||
if (mCount == NO_COUNT){
|
||||
mCount = startPos + mInitialRead;
|
||||
Thread t = new Thread(new QueryThread(mCursorState), "query thread");
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
|
||||
public int cursorPickFillWindowStartPosition(
|
||||
int cursorPosition, int cursorWindowCapacity) {
|
||||
return Math.max(cursorPosition - cursorWindowCapacity / 3, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase.CursorFactory;
|
||||
import net.sqlcipher.*;
|
||||
|
||||
/**
|
||||
* A driver for SQLiteCursors that is used to create them and gets notified
|
||||
* by the cursors it creates on significant events in their lifetimes.
|
||||
*/
|
||||
public interface SQLiteCursorDriver {
|
||||
/**
|
||||
* Executes the query returning a Cursor over the result set.
|
||||
*
|
||||
* @param factory The CursorFactory to use when creating the Cursors, or
|
||||
* null if standard SQLiteCursors should be returned.
|
||||
* @return a Cursor over the result set
|
||||
*/
|
||||
Cursor query(CursorFactory factory, String[] bindArgs);
|
||||
|
||||
/**
|
||||
* Called by a SQLiteCursor when it is released.
|
||||
*/
|
||||
void cursorDeactivated();
|
||||
|
||||
/**
|
||||
* Called by a SQLiteCursor when it is requeryed.
|
||||
*
|
||||
* @return The new count value.
|
||||
*/
|
||||
void cursorRequeried(android.database.Cursor cursor);
|
||||
|
||||
/**
|
||||
* Called by a SQLiteCursor when it it closed to destroy this object as well.
|
||||
*/
|
||||
void cursorClosed();
|
||||
|
||||
/**
|
||||
* Set new bind arguments. These will take effect in cursorRequeried().
|
||||
* @param bindArgs the new arguments
|
||||
*/
|
||||
public void setBindArguments(String[] bindArgs);
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
|
||||
/**
|
||||
* An exception that indicates that the SQLite database file is corrupt.
|
||||
*/
|
||||
public class SQLiteDatabaseCorruptException extends SQLiteException {
|
||||
public SQLiteDatabaseCorruptException() {}
|
||||
|
||||
public SQLiteDatabaseCorruptException(String error) {
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package net.sqlcipher.database;
|
||||
|
||||
/**
|
||||
* An interface to perform pre and post key operations against a database.
|
||||
*/
|
||||
public interface SQLiteDatabaseHook {
|
||||
/**
|
||||
* Called immediately before opening the database.
|
||||
*/
|
||||
void preKey(SQLiteDatabase database);
|
||||
/**
|
||||
* Called immediately after opening the database.
|
||||
*/
|
||||
void postKey(SQLiteDatabase database);
|
||||
}
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Provides debugging info about all SQLite databases running in the current process.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
public final class SQLiteDebug {
|
||||
/**
|
||||
* Controls the printing of SQL statements as they are executed.
|
||||
*/
|
||||
public static final boolean DEBUG_SQL_STATEMENTS =
|
||||
Log.isLoggable("SQLiteStatements", Log.VERBOSE);
|
||||
|
||||
/**
|
||||
* Controls the printing of wall-clock time taken to execute SQL statements
|
||||
* as they are executed.
|
||||
*/
|
||||
public static final boolean DEBUG_SQL_TIME =
|
||||
Log.isLoggable("SQLiteTime", Log.VERBOSE);
|
||||
|
||||
/**
|
||||
* Controls the printing of compiled-sql-statement cache stats.
|
||||
*/
|
||||
public static final boolean DEBUG_SQL_CACHE =
|
||||
Log.isLoggable("SQLiteCompiledSql", Log.VERBOSE);
|
||||
|
||||
/**
|
||||
* Controls the stack trace reporting of active cursors being
|
||||
* finalized.
|
||||
*/
|
||||
public static final boolean DEBUG_ACTIVE_CURSOR_FINALIZATION =
|
||||
Log.isLoggable("SQLiteCursorClosing", Log.VERBOSE);
|
||||
|
||||
/**
|
||||
* Controls the tracking of time spent holding the database lock.
|
||||
*/
|
||||
public static final boolean DEBUG_LOCK_TIME_TRACKING =
|
||||
Log.isLoggable("SQLiteLockTime", Log.VERBOSE);
|
||||
|
||||
/**
|
||||
* Controls the printing of stack traces when tracking the time spent holding the database lock.
|
||||
*/
|
||||
public static final boolean DEBUG_LOCK_TIME_TRACKING_STACK_TRACE =
|
||||
Log.isLoggable("SQLiteLockStackTrace", Log.VERBOSE);
|
||||
|
||||
/**
|
||||
* Contains statistics about the active pagers in the current process.
|
||||
*
|
||||
* @see #getPagerStats(PagerStats)
|
||||
*/
|
||||
public static class PagerStats {
|
||||
/** The total number of bytes in all pagers in the current process
|
||||
* @deprecated not used any longer
|
||||
*/
|
||||
@Deprecated
|
||||
public long totalBytes;
|
||||
/** The number of bytes in referenced pages in all pagers in the current process
|
||||
* @deprecated not used any longer
|
||||
* */
|
||||
@Deprecated
|
||||
public long referencedBytes;
|
||||
/** The number of bytes in all database files opened in the current process
|
||||
* @deprecated not used any longer
|
||||
*/
|
||||
@Deprecated
|
||||
public long databaseBytes;
|
||||
/** The number of pagers opened in the current process
|
||||
* @deprecated not used any longer
|
||||
*/
|
||||
@Deprecated
|
||||
public int numPagers;
|
||||
|
||||
/** the current amount of memory checked out by sqlite using sqlite3_malloc().
|
||||
* documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html
|
||||
*/
|
||||
public int memoryUsed;
|
||||
|
||||
/** the number of bytes of page cache allocation which could not be sattisfied by the
|
||||
* SQLITE_CONFIG_PAGECACHE buffer and where forced to overflow to sqlite3_malloc().
|
||||
* The returned value includes allocations that overflowed because they where too large
|
||||
* (they were larger than the "sz" parameter to SQLITE_CONFIG_PAGECACHE) and allocations
|
||||
* that overflowed because no space was left in the page cache.
|
||||
* documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html
|
||||
*/
|
||||
public int pageCacheOverflo;
|
||||
|
||||
/** records the largest memory allocation request handed to sqlite3.
|
||||
* documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html
|
||||
*/
|
||||
public int largestMemAlloc;
|
||||
|
||||
/** a list of {@link DbStats} - one for each main database opened by the applications
|
||||
* running on the android device
|
||||
*/
|
||||
public ArrayList<DbStats> dbStats;
|
||||
}
|
||||
|
||||
/**
|
||||
* contains statistics about a database
|
||||
*/
|
||||
public static class DbStats {
|
||||
/** name of the database */
|
||||
public String dbName;
|
||||
|
||||
/** the page size for the database */
|
||||
public long pageSize;
|
||||
|
||||
/** the database size */
|
||||
public long dbSize;
|
||||
|
||||
/** documented here http://www.sqlite.org/c3ref/c_dbstatus_lookaside_used.html */
|
||||
public int lookaside;
|
||||
|
||||
public DbStats(String dbName, long pageCount, long pageSize, int lookaside) {
|
||||
this.dbName = dbName;
|
||||
this.pageSize = pageSize;
|
||||
dbSize = (pageCount * pageSize) / 1024;
|
||||
this.lookaside = lookaside;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return all pager and database stats for the current process.
|
||||
* @return {@link PagerStats}
|
||||
*/
|
||||
public static PagerStats getDatabaseInfo() {
|
||||
PagerStats stats = new PagerStats();
|
||||
getPagerStats(stats);
|
||||
stats.dbStats = SQLiteDatabase.getDbStats();
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gathers statistics about all pagers in the current process.
|
||||
*/
|
||||
public static native void getPagerStats(PagerStats stats);
|
||||
|
||||
/**
|
||||
* Returns the size of the SQLite heap.
|
||||
* @return The size of the SQLite heap in bytes.
|
||||
*/
|
||||
public static native long getHeapSize();
|
||||
|
||||
/**
|
||||
* Returns the amount of allocated memory in the SQLite heap.
|
||||
* @return The allocated size in bytes.
|
||||
*/
|
||||
public static native long getHeapAllocatedSize();
|
||||
|
||||
/**
|
||||
* Returns the amount of free memory in the SQLite heap.
|
||||
* @return The freed size in bytes.
|
||||
*/
|
||||
public static native long getHeapFreeSize();
|
||||
|
||||
/**
|
||||
* Determines the number of dirty belonging to the SQLite
|
||||
* heap segments of this process. pages[0] returns the number of
|
||||
* shared pages, pages[1] returns the number of private pages
|
||||
*/
|
||||
public static native void getHeapDirtyPages(int[] pages);
|
||||
|
||||
private static int sNumActiveCursorsFinalized = 0;
|
||||
|
||||
/**
|
||||
* Returns the number of active cursors that have been finalized. This depends on the GC having
|
||||
* run but is still useful for tests.
|
||||
*/
|
||||
public static int getNumActiveCursorsFinalized() {
|
||||
return sNumActiveCursorsFinalized;
|
||||
}
|
||||
|
||||
static synchronized void notifyActiveCursorFinalized() {
|
||||
sNumActiveCursorsFinalized++;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
|
||||
import net.sqlcipher.Cursor;
|
||||
import net.sqlcipher.database.SQLiteDatabase.CursorFactory;
|
||||
|
||||
/**
|
||||
* A cursor driver that uses the given query directly.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class SQLiteDirectCursorDriver implements SQLiteCursorDriver {
|
||||
private String mEditTable;
|
||||
private SQLiteDatabase mDatabase;
|
||||
private Cursor mCursor;
|
||||
private String mSql;
|
||||
private SQLiteQuery mQuery;
|
||||
|
||||
public SQLiteDirectCursorDriver(SQLiteDatabase db, String sql, String editTable) {
|
||||
mDatabase = db;
|
||||
mEditTable = editTable;
|
||||
mSql = sql;
|
||||
}
|
||||
|
||||
public Cursor query(CursorFactory factory, Object[] args) {
|
||||
SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, 0, args);
|
||||
try {
|
||||
query.bindArguments(args);
|
||||
if (factory == null) {
|
||||
mCursor = new SQLiteCursor(mDatabase, this, mEditTable, query);
|
||||
} else {
|
||||
mCursor = factory.newCursor(mDatabase, this, mEditTable, query);
|
||||
}
|
||||
mQuery = query;
|
||||
query = null;
|
||||
return mCursor;
|
||||
} finally {
|
||||
// Make sure this object is cleaned up if something happens
|
||||
if (query != null) query.close();
|
||||
}
|
||||
}
|
||||
|
||||
public Cursor query(CursorFactory factory, String[] selectionArgs) {
|
||||
// Compile the query
|
||||
SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, 0, selectionArgs);
|
||||
|
||||
try {
|
||||
// Arg binding
|
||||
int numArgs = selectionArgs == null ? 0 : selectionArgs.length;
|
||||
for (int i = 0; i < numArgs; i++) {
|
||||
query.bindString(i + 1, selectionArgs[i]);
|
||||
}
|
||||
|
||||
// Create the cursor
|
||||
if (factory == null) {
|
||||
mCursor = new SQLiteCursor(mDatabase, this, mEditTable, query);
|
||||
|
||||
} else {
|
||||
mCursor = factory.newCursor(mDatabase, this, mEditTable, query);
|
||||
}
|
||||
|
||||
mQuery = query;
|
||||
query = null;
|
||||
return mCursor;
|
||||
} finally {
|
||||
// Make sure this object is cleaned up if something happens
|
||||
if (query != null) query.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void cursorClosed() {
|
||||
mCursor = null;
|
||||
}
|
||||
|
||||
public void setBindArguments(String[] bindArgs) {
|
||||
final int numArgs = bindArgs.length;
|
||||
for (int i = 0; i < numArgs; i++) {
|
||||
mQuery.bindString(i + 1, bindArgs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cursorDeactivated() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cursorRequeried(android.database.Cursor cursor) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SQLiteDirectCursorDriver: " + mSql;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
|
||||
/**
|
||||
* An exception that indicates that an IO error occured while accessing the
|
||||
* SQLite database file.
|
||||
*/
|
||||
public class SQLiteDiskIOException extends SQLiteException {
|
||||
public SQLiteDiskIOException() {}
|
||||
|
||||
public SQLiteDiskIOException(String error) {
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
|
||||
/**
|
||||
* An exception that indicates that the SQLite program is done.
|
||||
* Thrown when an operation that expects a row (such as {@link
|
||||
* SQLiteStatement#simpleQueryForString} or {@link
|
||||
* SQLiteStatement#simpleQueryForLong}) does not get one.
|
||||
*/
|
||||
public class SQLiteDoneException extends SQLiteException {
|
||||
public SQLiteDoneException() {}
|
||||
|
||||
public SQLiteDoneException(String error) {
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
|
||||
import net.sqlcipher.*;
|
||||
|
||||
/**
|
||||
* A SQLite exception that indicates there was an error with SQL parsing or execution.
|
||||
*/
|
||||
public class SQLiteException extends SQLException {
|
||||
public SQLiteException() {}
|
||||
|
||||
public SQLiteException(String error) {
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
|
||||
/**
|
||||
* An exception that indicates that the SQLite database is full.
|
||||
*/
|
||||
public class SQLiteFullException extends SQLiteException {
|
||||
public SQLiteFullException() {}
|
||||
|
||||
public SQLiteFullException(String error) {
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
|
||||
public class SQLiteMisuseException extends SQLiteException {
|
||||
public SQLiteMisuseException() {}
|
||||
|
||||
public SQLiteMisuseException(String error) {
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,399 @@
|
|||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import android.content.Context;
|
||||
import net.sqlcipher.DatabaseErrorHandler;
|
||||
import net.sqlcipher.DefaultDatabaseErrorHandler;
|
||||
import net.sqlcipher.database.SQLiteDatabaseHook;
|
||||
import net.sqlcipher.database.SQLiteDatabase.CursorFactory;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* A helper class to manage database creation and version management.
|
||||
* You create a subclass implementing {@link #onCreate}, {@link #onUpgrade} and
|
||||
* optionally {@link #onOpen}, and this class takes care of opening the database
|
||||
* if it exists, creating it if it does not, and upgrading it as necessary.
|
||||
* Transactions are used to make sure the database is always in a sensible state.
|
||||
* <p>For an example, see the NotePadProvider class in the NotePad sample application,
|
||||
* in the <em>samples/</em> directory of the SDK.</p>
|
||||
*/
|
||||
public abstract class SQLiteOpenHelper {
|
||||
private static final String TAG = SQLiteOpenHelper.class.getSimpleName();
|
||||
|
||||
private final Context mContext;
|
||||
private final String mName;
|
||||
private final CursorFactory mFactory;
|
||||
private final int mNewVersion;
|
||||
private final SQLiteDatabaseHook mHook;
|
||||
private final DatabaseErrorHandler mErrorHandler;
|
||||
private boolean mEnableWriteAheadLogging;
|
||||
private boolean mDeferSetWriteAheadLoggingEnabled;
|
||||
|
||||
private SQLiteDatabase mDatabase = null;
|
||||
private boolean mIsInitializing = false;
|
||||
|
||||
/**
|
||||
* Create a helper object to create, open, and/or manage a database.
|
||||
* This method always returns very quickly. The database is not actually
|
||||
* created or opened until one of {@link #getWritableDatabase} or
|
||||
* {@link #getReadableDatabase} is called.
|
||||
*
|
||||
* @param context to use to open or create the database
|
||||
* @param name of the database file, or null for an in-memory database
|
||||
* @param factory to use for creating cursor objects, or null for the default
|
||||
* @param version number of the database (starting at 1); if the database is older,
|
||||
* {@link #onUpgrade} will be used to upgrade the database
|
||||
*/
|
||||
public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
|
||||
this(context, name, factory, version, null, new DefaultDatabaseErrorHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a helper object to create, open, and/or manage a database.
|
||||
* The database is not actually created or opened until one of
|
||||
* {@link #getWritableDatabase} or {@link #getReadableDatabase} is called.
|
||||
*
|
||||
* @param context to use to open or create the database
|
||||
* @param name of the database file, or null for an in-memory database
|
||||
* @param factory to use for creating cursor objects, or null for the default
|
||||
* @param version number of the database (starting at 1); if the database is older,
|
||||
* {@link #onUpgrade} will be used to upgrade the database
|
||||
* @param hook to run on pre/post key events
|
||||
*/
|
||||
public SQLiteOpenHelper(Context context, String name, CursorFactory factory,
|
||||
int version, SQLiteDatabaseHook hook) {
|
||||
this(context, name, factory, version, hook, new DefaultDatabaseErrorHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a helper object to create, open, and/or manage a database.
|
||||
* The database is not actually created or opened until one of
|
||||
* {@link #getWritableDatabase} or {@link #getReadableDatabase} is called.
|
||||
*
|
||||
* <p>Accepts input param: a concrete instance of {@link DatabaseErrorHandler} to be
|
||||
* used to handle corruption when sqlite reports database corruption.</p>
|
||||
*
|
||||
* @param context to use to open or create the database
|
||||
* @param name of the database file, or null for an in-memory database
|
||||
* @param factory to use for creating cursor objects, or null for the default
|
||||
* @param version number of the database (starting at 1); if the database is older,
|
||||
* {@link #onUpgrade} will be used to upgrade the database
|
||||
* @param hook to run on pre/post key events
|
||||
* @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database
|
||||
* corruption.
|
||||
*/
|
||||
public SQLiteOpenHelper(Context context, String name, CursorFactory factory,
|
||||
int version, SQLiteDatabaseHook hook, DatabaseErrorHandler errorHandler) {
|
||||
if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);
|
||||
if (errorHandler == null) {
|
||||
throw new IllegalArgumentException("DatabaseErrorHandler param value can't be null.");
|
||||
}
|
||||
|
||||
mContext = context;
|
||||
mName = name;
|
||||
mFactory = factory;
|
||||
mNewVersion = version;
|
||||
mHook = hook;
|
||||
mErrorHandler = errorHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and/or open a database that will be used for reading and writing.
|
||||
* Once opened successfully, the database is cached, so you can call this
|
||||
* method every time you need to write to the database. Make sure to call
|
||||
* {@link #close} when you no longer need it.
|
||||
*
|
||||
* <p>Errors such as bad permissions or a full disk may cause this operation
|
||||
* to fail, but future attempts may succeed if the problem is fixed.</p>
|
||||
*
|
||||
* @throws SQLiteException if the database cannot be opened for writing
|
||||
* @return a read/write database object valid until {@link #close} is called
|
||||
*/
|
||||
|
||||
public synchronized SQLiteDatabase getWritableDatabase(String password) {
|
||||
return getWritableDatabase(password == null ? null : password.toCharArray());
|
||||
}
|
||||
|
||||
public synchronized SQLiteDatabase getWritableDatabase(char[] password) {
|
||||
return getWritableDatabase(password == null ? null : SQLiteDatabase.getBytes(password));
|
||||
}
|
||||
|
||||
public synchronized SQLiteDatabase getWritableDatabase(byte[] password) {
|
||||
if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
|
||||
return mDatabase; // The database is already open for business
|
||||
}
|
||||
|
||||
if (mIsInitializing) {
|
||||
throw new IllegalStateException("getWritableDatabase called recursively");
|
||||
}
|
||||
|
||||
// If we have a read-only database open, someone could be using it
|
||||
// (though they shouldn't), which would cause a lock to be held on
|
||||
// the file, and our attempts to open the database read-write would
|
||||
// fail waiting for the file lock. To prevent that, we acquire the
|
||||
// lock on the read-only database, which shuts out other users.
|
||||
|
||||
boolean success = false;
|
||||
SQLiteDatabase db = null;
|
||||
if (mDatabase != null) mDatabase.lock();
|
||||
try {
|
||||
mIsInitializing = true;
|
||||
if (mName == null) {
|
||||
db = SQLiteDatabase.create(null, "");
|
||||
} else {
|
||||
String path = mContext.getDatabasePath(mName).getPath();
|
||||
File dbPathFile = new File (path);
|
||||
if (!dbPathFile.exists()) {
|
||||
dbPathFile.getParentFile().mkdirs();
|
||||
}
|
||||
db = SQLiteDatabase.openOrCreateDatabase(path, password, mFactory, mHook, mErrorHandler);
|
||||
}
|
||||
if(mDeferSetWriteAheadLoggingEnabled) {
|
||||
mEnableWriteAheadLogging = db.enableWriteAheadLogging();
|
||||
}
|
||||
onConfigure(db);
|
||||
int version = db.getVersion();
|
||||
if (version != mNewVersion) {
|
||||
db.beginTransaction();
|
||||
try {
|
||||
if (version == 0) {
|
||||
onCreate(db);
|
||||
} else {
|
||||
if(version > mNewVersion) {
|
||||
onDowngrade(db, version, mNewVersion);
|
||||
} else {
|
||||
onUpgrade(db, version, mNewVersion);
|
||||
}
|
||||
}
|
||||
db.setVersion(mNewVersion);
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
onOpen(db);
|
||||
success = true;
|
||||
return db;
|
||||
} finally {
|
||||
mIsInitializing = false;
|
||||
if (success) {
|
||||
if (mDatabase != null) {
|
||||
try { mDatabase.close(); } catch (Exception e) { }
|
||||
mDatabase.unlock();
|
||||
}
|
||||
mDatabase = db;
|
||||
} else {
|
||||
if (mDatabase != null) mDatabase.unlock();
|
||||
if (db != null) db.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and/or open a database. This will be the same object returned by
|
||||
* {@link #getWritableDatabase} unless some problem, such as a full disk,
|
||||
* requires the database to be opened read-only. In that case, a read-only
|
||||
* database object will be returned. If the problem is fixed, a future call
|
||||
* to {@link #getWritableDatabase} may succeed, in which case the read-only
|
||||
* database object will be closed and the read/write object will be returned
|
||||
* in the future.
|
||||
*
|
||||
* @throws SQLiteException if the database cannot be opened
|
||||
* @return a database object valid until {@link #getWritableDatabase}
|
||||
* or {@link #close} is called.
|
||||
*/
|
||||
public synchronized SQLiteDatabase getReadableDatabase(String password) {
|
||||
return getReadableDatabase(password == null ? null : password.toCharArray());
|
||||
}
|
||||
|
||||
public synchronized SQLiteDatabase getReadableDatabase(char[] password) {
|
||||
return getReadableDatabase(password == null ? null : SQLiteDatabase.getBytes(password));
|
||||
}
|
||||
|
||||
public synchronized SQLiteDatabase getReadableDatabase(byte[] password) {
|
||||
if (mDatabase != null && mDatabase.isOpen()) {
|
||||
return mDatabase; // The database is already open for business
|
||||
}
|
||||
|
||||
if (mIsInitializing) {
|
||||
throw new IllegalStateException("getReadableDatabase called recursively");
|
||||
}
|
||||
|
||||
try {
|
||||
return getWritableDatabase(password);
|
||||
} catch (SQLiteException e) {
|
||||
if (mName == null) throw e; // Can't open a temp database read-only!
|
||||
Log.e(TAG, "Couldn't open " + mName + " for writing (will try read-only):", e);
|
||||
}
|
||||
|
||||
SQLiteDatabase db = null;
|
||||
try {
|
||||
mIsInitializing = true;
|
||||
String path = mContext.getDatabasePath(mName).getPath();
|
||||
File databasePath = new File(path);
|
||||
File databasesDirectory = new File(mContext.getDatabasePath(mName).getParent());
|
||||
|
||||
if(!databasesDirectory.exists()){
|
||||
databasesDirectory.mkdirs();
|
||||
}
|
||||
if(!databasePath.exists()){
|
||||
mIsInitializing = false;
|
||||
db = getWritableDatabase(password);
|
||||
mIsInitializing = true;
|
||||
db.close();
|
||||
}
|
||||
db = SQLiteDatabase.openDatabase(path, password, mFactory, SQLiteDatabase.OPEN_READONLY, mHook, mErrorHandler);
|
||||
if (db.getVersion() != mNewVersion) {
|
||||
throw new SQLiteException("Can't upgrade read-only database from version " +
|
||||
db.getVersion() + " to " + mNewVersion + ": " + path);
|
||||
}
|
||||
|
||||
onOpen(db);
|
||||
Log.w(TAG, "Opened " + mName + " in read-only mode");
|
||||
mDatabase = db;
|
||||
return mDatabase;
|
||||
} finally {
|
||||
mIsInitializing = false;
|
||||
if (db != null && db != mDatabase) db.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close any open database object.
|
||||
*/
|
||||
public synchronized void close() {
|
||||
if (mIsInitializing) throw new IllegalStateException("Closed during initialization");
|
||||
|
||||
if (mDatabase != null && mDatabase.isOpen()) {
|
||||
mDatabase.close();
|
||||
mDatabase = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the SQLite database being opened, as given to
|
||||
* the constructor.
|
||||
*/
|
||||
public String getDatabaseName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables the use of write-ahead logging for the database.
|
||||
*
|
||||
* Write-ahead logging cannot be used with read-only databases so the value of
|
||||
* this flag is ignored if the database is opened read-only.
|
||||
*
|
||||
* @param enabled True if write-ahead logging should be enabled, false if it
|
||||
* should be disabled.
|
||||
*
|
||||
* @see SQLiteDatabase#enableWriteAheadLogging()
|
||||
*/
|
||||
public void setWriteAheadLoggingEnabled(boolean enabled) {
|
||||
synchronized (this) {
|
||||
if (mEnableWriteAheadLogging != enabled) {
|
||||
if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
|
||||
if (enabled) {
|
||||
mDatabase.enableWriteAheadLogging();
|
||||
} else {
|
||||
mDatabase.disableWriteAheadLogging();
|
||||
}
|
||||
mEnableWriteAheadLogging = enabled;
|
||||
} else {
|
||||
mDeferSetWriteAheadLoggingEnabled = enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the database needs to be downgraded. This is strictly similar to
|
||||
* {@link #onUpgrade} method, but is called whenever current version is newer than requested one.
|
||||
* However, this method is not abstract, so it is not mandatory for a customer to
|
||||
* implement it. If not overridden, default implementation will reject downgrade and
|
||||
* throws SQLiteException
|
||||
*
|
||||
* <p>
|
||||
* This method executes within a transaction. If an exception is thrown, all changes
|
||||
* will automatically be rolled back.
|
||||
* </p>
|
||||
*
|
||||
* @param db The database.
|
||||
* @param oldVersion The old database version.
|
||||
* @param newVersion The new database version.
|
||||
*/
|
||||
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
throw new SQLiteException("Can't downgrade database from version " +
|
||||
oldVersion + " to " + newVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the database connection is being configured, to enable features
|
||||
* such as write-ahead logging or foreign key support.
|
||||
* <p>
|
||||
* This method is called before {@link #onCreate}, {@link #onUpgrade},
|
||||
* {@link #onDowngrade}, or {@link #onOpen} are called. It should not modify
|
||||
* the database except to configure the database connection as required.
|
||||
* </p><p>
|
||||
* This method should only call methods that configure the parameters of the
|
||||
* database connection, such as {@link SQLiteDatabase#enableWriteAheadLogging}
|
||||
* {@link SQLiteDatabase#setForeignKeyConstraintsEnabled},
|
||||
* {@link SQLiteDatabase#setLocale}, or executing PRAGMA statements.
|
||||
* </p>
|
||||
*
|
||||
* @param db The database.
|
||||
*/
|
||||
public void onConfigure(SQLiteDatabase db) {}
|
||||
|
||||
/**
|
||||
* Called when the database is created for the first time. This is where the
|
||||
* creation of tables and the initial population of the tables should happen.
|
||||
*
|
||||
* @param db The database.
|
||||
*/
|
||||
public abstract void onCreate(SQLiteDatabase db);
|
||||
|
||||
/**
|
||||
* Called when the database needs to be upgraded. The implementation
|
||||
* should use this method to drop tables, add tables, or do anything else it
|
||||
* needs to upgrade to the new schema version.
|
||||
*
|
||||
* <p>The SQLite ALTER TABLE documentation can be found
|
||||
* <a href="http://sqlite.org/lang_altertable.html">here</a>. If you add new columns
|
||||
* you can use ALTER TABLE to insert them into a live table. If you rename or remove columns
|
||||
* you can use ALTER TABLE to rename the old table, then create the new table and then
|
||||
* populate the new table with the contents of the old table.
|
||||
*
|
||||
* @param db The database.
|
||||
* @param oldVersion The old database version.
|
||||
* @param newVersion The new database version.
|
||||
*/
|
||||
public abstract void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);
|
||||
|
||||
/**
|
||||
* Called when the database has been opened.
|
||||
* Override method should check {@link SQLiteDatabase#isReadOnly} before
|
||||
* updating the database.
|
||||
*
|
||||
* @param db The database.
|
||||
*/
|
||||
public void onOpen(SQLiteDatabase db) {}
|
||||
}
|
||||
|
|
@ -0,0 +1,361 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
|
||||
import android.util.Log;
|
||||
import androidx.sqlite.db.SupportSQLiteProgram;
|
||||
|
||||
/**
|
||||
* A base class for compiled SQLite programs.
|
||||
*
|
||||
* SQLiteProgram is not internally synchronized so code using a SQLiteProgram from multiple
|
||||
* threads should perform its own synchronization when using the SQLiteProgram.
|
||||
*/
|
||||
public abstract class SQLiteProgram extends SQLiteClosable implements
|
||||
SupportSQLiteProgram {
|
||||
|
||||
private static final String TAG = "SQLiteProgram";
|
||||
|
||||
/** The database this program is compiled against.
|
||||
* @deprecated do not use this
|
||||
*/
|
||||
@Deprecated
|
||||
protected SQLiteDatabase mDatabase;
|
||||
|
||||
/** The SQL used to create this query */
|
||||
/* package */ final String mSql;
|
||||
|
||||
/**
|
||||
* Native linkage, do not modify. This comes from the database and should not be modified
|
||||
* in here or in the native code.
|
||||
* @deprecated do not use this
|
||||
*/
|
||||
@Deprecated
|
||||
protected long nHandle = 0;
|
||||
|
||||
/**
|
||||
* the SQLiteCompiledSql object for the given sql statement.
|
||||
*/
|
||||
private SQLiteCompiledSql mCompiledSql;
|
||||
|
||||
/**
|
||||
* SQLiteCompiledSql statement id is populated with the corresponding object from the above
|
||||
* member. This member is used by the native_bind_* methods
|
||||
* @deprecated do not use this
|
||||
*/
|
||||
@Deprecated
|
||||
protected long nStatement = 0;
|
||||
|
||||
/**
|
||||
* Indicates whether {@link #close()} has been called.
|
||||
*/
|
||||
boolean mClosed = false;
|
||||
|
||||
/* package */ SQLiteProgram(SQLiteDatabase db, String sql) {
|
||||
mDatabase = db;
|
||||
mSql = sql.trim();
|
||||
db.acquireReference();
|
||||
db.addSQLiteClosable(this);
|
||||
this.nHandle = db.mNativeHandle;
|
||||
int crudPrefixLength = 6;
|
||||
|
||||
// only cache CRUD statements
|
||||
String prefixSql = mSql.length() >= crudPrefixLength ? mSql.substring(0, crudPrefixLength) : mSql;
|
||||
if (!prefixSql.equalsIgnoreCase("INSERT") && !prefixSql.equalsIgnoreCase("UPDATE") &&
|
||||
!prefixSql.equalsIgnoreCase("REPLAC") &&
|
||||
!prefixSql.equalsIgnoreCase("DELETE") && !prefixSql.equalsIgnoreCase("SELECT")) {
|
||||
mCompiledSql = new SQLiteCompiledSql(db, sql);
|
||||
nStatement = mCompiledSql.nStatement;
|
||||
// since it is not in the cache, no need to acquire() it.
|
||||
return;
|
||||
}
|
||||
|
||||
// it is not pragma
|
||||
mCompiledSql = db.getCompiledStatementForSql(sql);
|
||||
if (mCompiledSql == null) {
|
||||
// create a new compiled-sql obj
|
||||
mCompiledSql = new SQLiteCompiledSql(db, sql);
|
||||
|
||||
// add it to the cache of compiled-sqls
|
||||
// but before adding it and thus making it available for anyone else to use it,
|
||||
// make sure it is acquired by me.
|
||||
mCompiledSql.acquire();
|
||||
db.addToCompiledQueries(sql, mCompiledSql);
|
||||
if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
|
||||
Log.v(TAG, "Created DbObj (id#" + mCompiledSql.nStatement +
|
||||
") for sql: " + sql);
|
||||
}
|
||||
} else {
|
||||
// it is already in compiled-sql cache.
|
||||
// try to acquire the object.
|
||||
if (!mCompiledSql.acquire()) {
|
||||
long last = mCompiledSql.nStatement;
|
||||
// the SQLiteCompiledSql in cache is in use by some other SQLiteProgram object.
|
||||
// we can't have two different SQLiteProgam objects can't share the same
|
||||
// CompiledSql object. create a new one.
|
||||
// finalize it when I am done with it in "this" object.
|
||||
mCompiledSql = new SQLiteCompiledSql(db, sql);
|
||||
if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
|
||||
Log.v(TAG, "** possible bug ** Created NEW DbObj (id#" +
|
||||
mCompiledSql.nStatement +
|
||||
") because the previously created DbObj (id#" + last +
|
||||
") was not released for sql:" + sql);
|
||||
}
|
||||
// since it is not in the cache, no need to acquire() it.
|
||||
}
|
||||
}
|
||||
nStatement = mCompiledSql.nStatement;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAllReferencesReleased() {
|
||||
releaseCompiledSqlIfNotInCache();
|
||||
mDatabase.releaseReference();
|
||||
mDatabase.removeSQLiteClosable(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAllReferencesReleasedFromContainer() {
|
||||
releaseCompiledSqlIfNotInCache();
|
||||
mDatabase.releaseReference();
|
||||
}
|
||||
|
||||
private void releaseCompiledSqlIfNotInCache() {
|
||||
if (mCompiledSql == null) {
|
||||
return;
|
||||
}
|
||||
synchronized(mDatabase.mCompiledQueries) {
|
||||
if (!mDatabase.mCompiledQueries.containsValue(mCompiledSql)) {
|
||||
// it is NOT in compiled-sql cache. i.e., responsibility of
|
||||
// releasing this statement is on me.
|
||||
mCompiledSql.releaseSqlStatement();
|
||||
mCompiledSql = null;
|
||||
nStatement = 0;
|
||||
} else {
|
||||
// it is in compiled-sql cache. reset its CompiledSql#mInUse flag
|
||||
mCompiledSql.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a unique identifier for this program.
|
||||
*
|
||||
* @return a unique identifier for this program
|
||||
*/
|
||||
public final long getUniqueId() {
|
||||
return nStatement;
|
||||
}
|
||||
|
||||
/* package */ String getSqlString() {
|
||||
return mSql;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This method is deprecated and must not be used.
|
||||
*
|
||||
* @param sql the SQL string to compile
|
||||
* @param forceCompilation forces the SQL to be recompiled in the event that there is an
|
||||
* existing compiled SQL program already around
|
||||
*/
|
||||
@Deprecated
|
||||
protected void compile(String sql, boolean forceCompilation) {
|
||||
// TODO is there a need for this?
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a NULL value to this statement. The value remains bound until
|
||||
* {@link #clearBindings} is called.
|
||||
*
|
||||
* @param index The 1-based index to the parameter to bind null to
|
||||
*/
|
||||
@Override
|
||||
public void bindNull(int index) {
|
||||
if (mClosed) {
|
||||
throw new IllegalStateException("program already closed");
|
||||
}
|
||||
if (!mDatabase.isOpen()) {
|
||||
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
|
||||
}
|
||||
acquireReference();
|
||||
try {
|
||||
native_bind_null(index);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a long value to this statement. The value remains bound until
|
||||
* {@link #clearBindings} is called.
|
||||
*
|
||||
* @param index The 1-based index to the parameter to bind
|
||||
* @param value The value to bind
|
||||
*/
|
||||
@Override
|
||||
public void bindLong(int index, long value) {
|
||||
if (mClosed) {
|
||||
throw new IllegalStateException("program already closed");
|
||||
}
|
||||
if (!mDatabase.isOpen()) {
|
||||
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
|
||||
}
|
||||
acquireReference();
|
||||
try {
|
||||
native_bind_long(index, value);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a double value to this statement. The value remains bound until
|
||||
* {@link #clearBindings} is called.
|
||||
*
|
||||
* @param index The 1-based index to the parameter to bind
|
||||
* @param value The value to bind
|
||||
*/
|
||||
@Override
|
||||
public void bindDouble(int index, double value) {
|
||||
if (mClosed) {
|
||||
throw new IllegalStateException("program already closed");
|
||||
}
|
||||
if (!mDatabase.isOpen()) {
|
||||
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
|
||||
}
|
||||
acquireReference();
|
||||
try {
|
||||
native_bind_double(index, value);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a String value to this statement. The value remains bound until
|
||||
* {@link #clearBindings} is called.
|
||||
*
|
||||
* @param index The 1-based index to the parameter to bind
|
||||
* @param value The value to bind
|
||||
*/
|
||||
@Override
|
||||
public void bindString(int index, String value) {
|
||||
if (value == null) {
|
||||
throw new IllegalArgumentException("the bind value at index " + index + " is null");
|
||||
}
|
||||
if (mClosed) {
|
||||
throw new IllegalStateException("program already closed");
|
||||
}
|
||||
if (!mDatabase.isOpen()) {
|
||||
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
|
||||
}
|
||||
acquireReference();
|
||||
try {
|
||||
native_bind_string(index, value);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a byte array value to this statement. The value remains bound until
|
||||
* {@link #clearBindings} is called.
|
||||
*
|
||||
* @param index The 1-based index to the parameter to bind
|
||||
* @param value The value to bind
|
||||
*/
|
||||
@Override
|
||||
public void bindBlob(int index, byte[] value) {
|
||||
if (value == null) {
|
||||
throw new IllegalArgumentException("the bind value at index " + index + " is null");
|
||||
}
|
||||
if (mClosed) {
|
||||
throw new IllegalStateException("program already closed");
|
||||
}
|
||||
if (!mDatabase.isOpen()) {
|
||||
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
|
||||
}
|
||||
acquireReference();
|
||||
try {
|
||||
native_bind_blob(index, value);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all existing bindings. Unset bindings are treated as NULL.
|
||||
*/
|
||||
@Override
|
||||
public void clearBindings() {
|
||||
if (mClosed) {
|
||||
throw new IllegalStateException("program already closed");
|
||||
}
|
||||
if (!mDatabase.isOpen()) {
|
||||
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
|
||||
}
|
||||
acquireReference();
|
||||
try {
|
||||
native_clear_bindings();
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Release this program's resources, making it invalid.
|
||||
*/
|
||||
public void close() {
|
||||
if (mClosed) {
|
||||
return;
|
||||
}
|
||||
if (!mDatabase.isOpen()) {
|
||||
return;
|
||||
}
|
||||
mDatabase.lock();
|
||||
try {
|
||||
releaseReference();
|
||||
} finally {
|
||||
mDatabase.unlock();
|
||||
}
|
||||
mClosed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This method is deprecated and must not be used.
|
||||
* Compiles SQL into a SQLite program.
|
||||
*
|
||||
* <P>The database lock must be held when calling this method.
|
||||
* @param sql The SQL to compile.
|
||||
*/
|
||||
@Deprecated
|
||||
protected final native void native_compile(String sql);
|
||||
|
||||
/**
|
||||
* @deprecated This method is deprecated and must not be used.
|
||||
*/
|
||||
@Deprecated
|
||||
protected final native void native_finalize();
|
||||
|
||||
protected final native void native_bind_null(int index);
|
||||
protected final native void native_bind_long(int index, long value);
|
||||
protected final native void native_bind_double(int index, double value);
|
||||
protected final native void native_bind_string(int index, String value);
|
||||
protected final native void native_bind_blob(int index, byte[] value);
|
||||
private final native void native_clear_bindings();
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
import net.sqlcipher.*;
|
||||
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* A SQLite program that represents a query that reads the resulting rows into a CursorWindow.
|
||||
* This class is used by SQLiteCursor and isn't useful itself.
|
||||
*
|
||||
* SQLiteQuery is not internally synchronized so code using a SQLiteQuery from multiple
|
||||
* threads should perform its own synchronization when using the SQLiteQuery.
|
||||
*/
|
||||
public class SQLiteQuery extends SQLiteProgram {
|
||||
private static final String TAG = "Cursor";
|
||||
|
||||
/** The index of the unbound OFFSET parameter */
|
||||
private int mOffsetIndex;
|
||||
|
||||
/** Args to bind on requery */
|
||||
private String[] mBindArgs;
|
||||
private Object[] mObjectBindArgs;
|
||||
|
||||
/**
|
||||
* Create a persistent query object.
|
||||
*
|
||||
* @param db The database that this query object is associated with
|
||||
* @param query The SQL string for this query.
|
||||
* @param offsetIndex The 1-based index to the OFFSET parameter,
|
||||
*/
|
||||
/* package */ SQLiteQuery(SQLiteDatabase db, String query, int offsetIndex, String[] bindArgs) {
|
||||
super(db, query);
|
||||
|
||||
mOffsetIndex = offsetIndex;
|
||||
mBindArgs = bindArgs;
|
||||
}
|
||||
|
||||
SQLiteQuery(SQLiteDatabase db, String query, int offsetIndex, Object[] bindArgs) {
|
||||
super(db, query);
|
||||
mOffsetIndex = offsetIndex;
|
||||
mObjectBindArgs = bindArgs;
|
||||
int length = mObjectBindArgs != null ? mObjectBindArgs.length : 0;
|
||||
mBindArgs = new String[length];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads rows into a buffer. This method acquires the database lock.
|
||||
*
|
||||
* @param window The window to fill into
|
||||
* @return number of total rows in the query
|
||||
*/
|
||||
/* package */
|
||||
int fillWindow(CursorWindow window,
|
||||
int maxRead, int lastPos) {
|
||||
long timeStart = SystemClock.uptimeMillis();
|
||||
mDatabase.lock();
|
||||
try {
|
||||
acquireReference();
|
||||
try {
|
||||
window.acquireReference();
|
||||
// if the start pos is not equal to 0, then most likely window is
|
||||
// too small for the data set, loading by another thread
|
||||
// is not safe in this situation. the native code will ignore maxRead
|
||||
int numRows = native_fill_window(window,
|
||||
window.getStartPosition(),
|
||||
window.getRequiredPosition(),
|
||||
mOffsetIndex,
|
||||
maxRead, lastPos);
|
||||
|
||||
// Logging
|
||||
if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
|
||||
Log.d(TAG, "fillWindow(): " + mSql);
|
||||
}
|
||||
return numRows;
|
||||
} catch (IllegalStateException e){
|
||||
// simply ignore it
|
||||
return 0;
|
||||
} catch (SQLiteDatabaseCorruptException e) {
|
||||
mDatabase.onCorruption();
|
||||
throw e;
|
||||
} finally {
|
||||
window.releaseReference();
|
||||
}
|
||||
} finally {
|
||||
releaseReference();
|
||||
mDatabase.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the column count for the statement. Only valid on query based
|
||||
* statements. The database must be locked
|
||||
* when calling this method.
|
||||
*
|
||||
* @return The number of column in the statement's result set.
|
||||
*/
|
||||
/* package */ int columnCountLocked() {
|
||||
acquireReference();
|
||||
try {
|
||||
return native_column_count();
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the column name for the given column index. The database must be locked
|
||||
* when calling this method.
|
||||
*
|
||||
* @param columnIndex the index of the column to get the name for
|
||||
* @return The requested column's name
|
||||
*/
|
||||
/* package */ String columnNameLocked(int columnIndex) {
|
||||
acquireReference();
|
||||
try {
|
||||
return native_column_name(columnIndex);
|
||||
} finally {
|
||||
releaseReference();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SQLiteQuery: " + mSql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by SQLiteCursor when it is requeried.
|
||||
*/
|
||||
/* package */ void requery() {
|
||||
if (mBindArgs != null) {
|
||||
int len = mBindArgs.length;
|
||||
try {
|
||||
if(mObjectBindArgs != null) {
|
||||
bindArguments(mObjectBindArgs);
|
||||
} else {
|
||||
for (int i = 0; i < len; i++) {
|
||||
super.bindString(i + 1, mBindArgs[i]);
|
||||
}
|
||||
}
|
||||
} catch (SQLiteMisuseException e) {
|
||||
StringBuilder errMsg = new StringBuilder("mSql " + mSql);
|
||||
for (int i = 0; i < len; i++) {
|
||||
errMsg.append(" ");
|
||||
errMsg.append(mBindArgs[i]);
|
||||
}
|
||||
errMsg.append(" ");
|
||||
IllegalStateException leakProgram = new IllegalStateException(
|
||||
errMsg.toString(), e);
|
||||
throw leakProgram;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindNull(int index) {
|
||||
mBindArgs[index - 1] = null;
|
||||
if (!mClosed) super.bindNull(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindLong(int index, long value) {
|
||||
mBindArgs[index - 1] = Long.toString(value);
|
||||
if (!mClosed) super.bindLong(index, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindDouble(int index, double value) {
|
||||
mBindArgs[index - 1] = Double.toString(value);
|
||||
if (!mClosed) super.bindDouble(index, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindString(int index, String value) {
|
||||
mBindArgs[index - 1] = value;
|
||||
if (!mClosed) super.bindString(index, value);
|
||||
}
|
||||
|
||||
public void bindArguments(Object[] args){
|
||||
if(args != null && args.length > 0){
|
||||
for(int i = 0; i < args.length; i++){
|
||||
Object value = args[i];
|
||||
if(value == null){
|
||||
bindNull(i + 1);
|
||||
} else if (value instanceof Double) {
|
||||
bindDouble(i + 1, (Double)value);
|
||||
} else if (value instanceof Float) {
|
||||
float number = ((Number)value).floatValue();
|
||||
bindDouble(i + 1, Double.valueOf(number));
|
||||
} else if (value instanceof Long) {
|
||||
bindLong(i + 1, (Long)value);
|
||||
} else if(value instanceof Integer) {
|
||||
int number = ((Number) value).intValue();
|
||||
bindLong(i + 1, Long.valueOf(number));
|
||||
} else if (value instanceof Boolean) {
|
||||
bindLong(i + 1, (Boolean)value ? 1 : 0);
|
||||
} else if (value instanceof byte[]) {
|
||||
bindBlob(i + 1, (byte[])value);
|
||||
} else {
|
||||
bindString(i + 1, value.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final native int native_fill_window(CursorWindow window,
|
||||
int startPos, int requiredPos,
|
||||
int offsetParam, int maxRead,
|
||||
int lastPos);
|
||||
|
||||
private final native int native_column_count();
|
||||
|
||||
private final native String native_column_name(int columnIndex);
|
||||
}
|
||||
|
|
@ -0,0 +1,550 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
|
||||
import net.sqlcipher.*;
|
||||
|
||||
import android.provider.BaseColumns;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* This is a convience class that helps build SQL queries to be sent to
|
||||
* {@link SQLiteDatabase} objects.
|
||||
*/
|
||||
public class SQLiteQueryBuilder
|
||||
{
|
||||
private static final String TAG = "SQLiteQueryBuilder";
|
||||
private static final Pattern sLimitPattern =
|
||||
Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?");
|
||||
|
||||
private Map<String, String> mProjectionMap = null;
|
||||
private String mTables = "";
|
||||
private StringBuilder mWhereClause = null; // lazily created
|
||||
private boolean mDistinct;
|
||||
private SQLiteDatabase.CursorFactory mFactory;
|
||||
private boolean mStrictProjectionMap;
|
||||
|
||||
public SQLiteQueryBuilder() {
|
||||
mDistinct = false;
|
||||
mFactory = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the query as DISTINCT.
|
||||
*
|
||||
* @param distinct if true the query is DISTINCT, otherwise it isn't
|
||||
*/
|
||||
public void setDistinct(boolean distinct) {
|
||||
mDistinct = distinct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of tables being queried
|
||||
*
|
||||
* @return the list of tables being queried
|
||||
*/
|
||||
public String getTables() {
|
||||
return mTables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of tables to query. Multiple tables can be specified to perform a join.
|
||||
* For example:
|
||||
* setTables("foo, bar")
|
||||
* setTables("foo LEFT OUTER JOIN bar ON (foo.id = bar.foo_id)")
|
||||
*
|
||||
* @param inTables the list of tables to query on
|
||||
*/
|
||||
public void setTables(String inTables) {
|
||||
mTables = inTables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a chunk to the WHERE clause of the query. All chunks appended are surrounded
|
||||
* by parenthesis and ANDed with the selection passed to {@link #query}. The final
|
||||
* WHERE clause looks like:
|
||||
*
|
||||
* WHERE (<append chunk 1><append chunk2>) AND (<query() selection parameter>)
|
||||
*
|
||||
* @param inWhere the chunk of text to append to the WHERE clause.
|
||||
*/
|
||||
public void appendWhere(CharSequence inWhere) {
|
||||
if (mWhereClause == null) {
|
||||
mWhereClause = new StringBuilder(inWhere.length() + 16);
|
||||
}
|
||||
if (mWhereClause.length() == 0) {
|
||||
mWhereClause.append('(');
|
||||
}
|
||||
mWhereClause.append(inWhere);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a chunk to the WHERE clause of the query. All chunks appended are surrounded
|
||||
* by parenthesis and ANDed with the selection passed to {@link #query}. The final
|
||||
* WHERE clause looks like:
|
||||
*
|
||||
* WHERE (<append chunk 1><append chunk2>) AND (<query() selection parameter>)
|
||||
*
|
||||
* @param inWhere the chunk of text to append to the WHERE clause. it will be escaped
|
||||
* to avoid SQL injection attacks
|
||||
*/
|
||||
public void appendWhereEscapeString(String inWhere) {
|
||||
if (mWhereClause == null) {
|
||||
mWhereClause = new StringBuilder(inWhere.length() + 16);
|
||||
}
|
||||
if (mWhereClause.length() == 0) {
|
||||
mWhereClause.append('(');
|
||||
}
|
||||
DatabaseUtils.appendEscapedSQLString(mWhereClause, inWhere);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the projection map for the query. The projection map maps
|
||||
* from column names that the caller passes into query to database
|
||||
* column names. This is useful for renaming columns as well as
|
||||
* disambiguating column names when doing joins. For example you
|
||||
* could map "name" to "people.name". If a projection map is set
|
||||
* it must contain all column names the user may request, even if
|
||||
* the key and value are the same.
|
||||
*
|
||||
* @param columnMap maps from the user column names to the database column names
|
||||
*/
|
||||
public void setProjectionMap(Map<String, String> columnMap) {
|
||||
mProjectionMap = columnMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cursor factory to be used for the query. You can use
|
||||
* one factory for all queries on a database but it is normally
|
||||
* easier to specify the factory when doing this query. @param
|
||||
* factory the factor to use
|
||||
*/
|
||||
public void setCursorFactory(SQLiteDatabase.CursorFactory factory) {
|
||||
mFactory = factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void setStrictProjectionMap(boolean flag) {
|
||||
mStrictProjectionMap = flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an SQL query string from the given clauses.
|
||||
*
|
||||
* @param distinct true if you want each row to be unique, false otherwise.
|
||||
* @param tables The table names to compile the query against.
|
||||
* @param columns A list of which columns to return. Passing null will
|
||||
* return all columns, which is discouraged to prevent reading
|
||||
* data from storage that isn't going to be used.
|
||||
* @param where A filter declaring which rows to return, formatted as an SQL
|
||||
* WHERE clause (excluding the WHERE itself). Passing null will
|
||||
* return all rows for the given URL.
|
||||
* @param groupBy A filter declaring how to group rows, formatted as an SQL
|
||||
* GROUP BY clause (excluding the GROUP BY itself). Passing null
|
||||
* will cause the rows to not be grouped.
|
||||
* @param having A filter declare which row groups to include in the cursor,
|
||||
* if row grouping is being used, formatted as an SQL HAVING
|
||||
* clause (excluding the HAVING itself). Passing null will cause
|
||||
* all row groups to be included, and is required when row
|
||||
* grouping is not being used.
|
||||
* @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
|
||||
* (excluding the ORDER BY itself). Passing null will use the
|
||||
* default sort order, which may be unordered.
|
||||
* @param limit Limits the number of rows returned by the query,
|
||||
* formatted as LIMIT clause. Passing null denotes no LIMIT clause.
|
||||
* @return the SQL query string
|
||||
*/
|
||||
public static String buildQueryString(
|
||||
boolean distinct, String tables, String[] columns, String where,
|
||||
String groupBy, String having, String orderBy, String limit) {
|
||||
if (TextUtils.isEmpty(groupBy) && !TextUtils.isEmpty(having)) {
|
||||
throw new IllegalArgumentException(
|
||||
"HAVING clauses are only permitted when using a groupBy clause");
|
||||
}
|
||||
if (!TextUtils.isEmpty(limit) && !sLimitPattern.matcher(limit).matches()) {
|
||||
throw new IllegalArgumentException("invalid LIMIT clauses:" + limit);
|
||||
}
|
||||
|
||||
StringBuilder query = new StringBuilder(120);
|
||||
|
||||
query.append("SELECT ");
|
||||
if (distinct) {
|
||||
query.append("DISTINCT ");
|
||||
}
|
||||
if (columns != null && columns.length != 0) {
|
||||
appendColumns(query, columns);
|
||||
} else {
|
||||
query.append("* ");
|
||||
}
|
||||
query.append("FROM ");
|
||||
query.append(tables);
|
||||
appendClause(query, " WHERE ", where);
|
||||
appendClause(query, " GROUP BY ", groupBy);
|
||||
appendClause(query, " HAVING ", having);
|
||||
appendClause(query, " ORDER BY ", orderBy);
|
||||
appendClause(query, " LIMIT ", limit);
|
||||
|
||||
return query.toString();
|
||||
}
|
||||
|
||||
private static void appendClause(StringBuilder s, String name, String clause) {
|
||||
if (!TextUtils.isEmpty(clause)) {
|
||||
s.append(name);
|
||||
s.append(clause);
|
||||
}
|
||||
}
|
||||
|
||||
private static void appendClauseEscapeClause(StringBuilder s, String name, String clause) {
|
||||
if (!TextUtils.isEmpty(clause)) {
|
||||
s.append(name);
|
||||
DatabaseUtils.appendEscapedSQLString(s, clause);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the names that are non-null in columns to s, separating
|
||||
* them with commas.
|
||||
*/
|
||||
public static void appendColumns(StringBuilder s, String[] columns) {
|
||||
int n = columns.length;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
String column = columns[i];
|
||||
|
||||
if (column != null) {
|
||||
if (i > 0) {
|
||||
s.append(", ");
|
||||
}
|
||||
s.append(column);
|
||||
}
|
||||
}
|
||||
s.append(' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a query by combining all current settings and the
|
||||
* information passed into this method.
|
||||
*
|
||||
* @param db the database to query on
|
||||
* @param projectionIn A list of which columns to return. Passing
|
||||
* null will return all columns, which is discouraged to prevent
|
||||
* reading data from storage that isn't going to be used.
|
||||
* @param selection A filter declaring which rows to return,
|
||||
* formatted as an SQL WHERE clause (excluding the WHERE
|
||||
* itself). Passing null will return all rows for the given URL.
|
||||
* @param selectionArgs You may include ?s in selection, which
|
||||
* will be replaced by the values from selectionArgs, in order
|
||||
* that they appear in the selection. The values will be bound
|
||||
* as Strings.
|
||||
* @param groupBy A filter declaring how to group rows, formatted
|
||||
* as an SQL GROUP BY clause (excluding the GROUP BY
|
||||
* itself). Passing null will cause the rows to not be grouped.
|
||||
* @param having A filter declare which row groups to include in
|
||||
* the cursor, if row grouping is being used, formatted as an
|
||||
* SQL HAVING clause (excluding the HAVING itself). Passing
|
||||
* null will cause all row groups to be included, and is
|
||||
* required when row grouping is not being used.
|
||||
* @param sortOrder How to order the rows, formatted as an SQL
|
||||
* ORDER BY clause (excluding the ORDER BY itself). Passing null
|
||||
* will use the default sort order, which may be unordered.
|
||||
* @return a cursor over the result set
|
||||
* @see android.content.ContentResolver#query(android.net.Uri, String[],
|
||||
* String, String[], String)
|
||||
*/
|
||||
public Cursor query(SQLiteDatabase db, String[] projectionIn,
|
||||
String selection, String[] selectionArgs, String groupBy,
|
||||
String having, String sortOrder) {
|
||||
return query(db, projectionIn, selection, selectionArgs, groupBy, having, sortOrder,
|
||||
null /* limit */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a query by combining all current settings and the
|
||||
* information passed into this method.
|
||||
*
|
||||
* @param db the database to query on
|
||||
* @param projectionIn A list of which columns to return. Passing
|
||||
* null will return all columns, which is discouraged to prevent
|
||||
* reading data from storage that isn't going to be used.
|
||||
* @param selection A filter declaring which rows to return,
|
||||
* formatted as an SQL WHERE clause (excluding the WHERE
|
||||
* itself). Passing null will return all rows for the given URL.
|
||||
* @param selectionArgs You may include ?s in selection, which
|
||||
* will be replaced by the values from selectionArgs, in order
|
||||
* that they appear in the selection. The values will be bound
|
||||
* as Strings.
|
||||
* @param groupBy A filter declaring how to group rows, formatted
|
||||
* as an SQL GROUP BY clause (excluding the GROUP BY
|
||||
* itself). Passing null will cause the rows to not be grouped.
|
||||
* @param having A filter declare which row groups to include in
|
||||
* the cursor, if row grouping is being used, formatted as an
|
||||
* SQL HAVING clause (excluding the HAVING itself). Passing
|
||||
* null will cause all row groups to be included, and is
|
||||
* required when row grouping is not being used.
|
||||
* @param sortOrder How to order the rows, formatted as an SQL
|
||||
* ORDER BY clause (excluding the ORDER BY itself). Passing null
|
||||
* will use the default sort order, which may be unordered.
|
||||
* @param limit Limits the number of rows returned by the query,
|
||||
* formatted as LIMIT clause. Passing null denotes no LIMIT clause.
|
||||
* @return a cursor over the result set
|
||||
* @see android.content.ContentResolver#query(android.net.Uri, String[],
|
||||
* String, String[], String)
|
||||
*/
|
||||
public Cursor query(SQLiteDatabase db, String[] projectionIn,
|
||||
String selection, String[] selectionArgs, String groupBy,
|
||||
String having, String sortOrder, String limit) {
|
||||
if (mTables == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String sql = buildQuery(
|
||||
projectionIn, selection, selectionArgs, groupBy, having,
|
||||
sortOrder, limit);
|
||||
|
||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||
Log.d(TAG, "Performing query: " + sql);
|
||||
}
|
||||
return db.rawQueryWithFactory(
|
||||
mFactory, sql, selectionArgs,
|
||||
SQLiteDatabase.findEditTable(mTables));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a SELECT statement suitable for use in a group of
|
||||
* SELECT statements that will be joined through UNION operators
|
||||
* in buildUnionQuery.
|
||||
*
|
||||
* @param projectionIn A list of which columns to return. Passing
|
||||
* null will return all columns, which is discouraged to
|
||||
* prevent reading data from storage that isn't going to be
|
||||
* used.
|
||||
* @param selection A filter declaring which rows to return,
|
||||
* formatted as an SQL WHERE clause (excluding the WHERE
|
||||
* itself). Passing null will return all rows for the given
|
||||
* URL.
|
||||
* @param selectionArgs You may include ?s in selection, which
|
||||
* will be replaced by the values from selectionArgs, in order
|
||||
* that they appear in the selection. The values will be bound
|
||||
* as Strings.
|
||||
* @param groupBy A filter declaring how to group rows, formatted
|
||||
* as an SQL GROUP BY clause (excluding the GROUP BY itself).
|
||||
* Passing null will cause the rows to not be grouped.
|
||||
* @param having A filter declare which row groups to include in
|
||||
* the cursor, if row grouping is being used, formatted as an
|
||||
* SQL HAVING clause (excluding the HAVING itself). Passing
|
||||
* null will cause all row groups to be included, and is
|
||||
* required when row grouping is not being used.
|
||||
* @param sortOrder How to order the rows, formatted as an SQL
|
||||
* ORDER BY clause (excluding the ORDER BY itself). Passing null
|
||||
* will use the default sort order, which may be unordered.
|
||||
* @param limit Limits the number of rows returned by the query,
|
||||
* formatted as LIMIT clause. Passing null denotes no LIMIT clause.
|
||||
* @return the resulting SQL SELECT statement
|
||||
*/
|
||||
public String buildQuery(
|
||||
String[] projectionIn, String selection, String[] selectionArgs,
|
||||
String groupBy, String having, String sortOrder, String limit) {
|
||||
String[] projection = computeProjection(projectionIn);
|
||||
|
||||
StringBuilder where = new StringBuilder();
|
||||
boolean hasBaseWhereClause = mWhereClause != null && mWhereClause.length() > 0;
|
||||
|
||||
if (hasBaseWhereClause) {
|
||||
where.append(mWhereClause.toString());
|
||||
where.append(')');
|
||||
}
|
||||
|
||||
// Tack on the user's selection, if present.
|
||||
if (selection != null && selection.length() > 0) {
|
||||
if (hasBaseWhereClause) {
|
||||
where.append(" AND ");
|
||||
}
|
||||
|
||||
where.append('(');
|
||||
where.append(selection);
|
||||
where.append(')');
|
||||
}
|
||||
|
||||
return buildQueryString(
|
||||
mDistinct, mTables, projection, where.toString(),
|
||||
groupBy, having, sortOrder, limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a SELECT statement suitable for use in a group of
|
||||
* SELECT statements that will be joined through UNION operators
|
||||
* in buildUnionQuery.
|
||||
*
|
||||
* @param typeDiscriminatorColumn the name of the result column
|
||||
* whose cells will contain the name of the table from which
|
||||
* each row was drawn.
|
||||
* @param unionColumns the names of the columns to appear in the
|
||||
* result. This may include columns that do not appear in the
|
||||
* table this SELECT is querying (i.e. mTables), but that do
|
||||
* appear in one of the other tables in the UNION query that we
|
||||
* are constructing.
|
||||
* @param columnsPresentInTable a Set of the names of the columns
|
||||
* that appear in this table (i.e. in the table whose name is
|
||||
* mTables). Since columns in unionColumns include columns that
|
||||
* appear only in other tables, we use this array to distinguish
|
||||
* which ones actually are present. Other columns will have
|
||||
* NULL values for results from this subquery.
|
||||
* @param computedColumnsOffset all columns in unionColumns before
|
||||
* this index are included under the assumption that they're
|
||||
* computed and therefore won't appear in columnsPresentInTable,
|
||||
* e.g. "date * 1000 as normalized_date"
|
||||
* @param typeDiscriminatorValue the value used for the
|
||||
* type-discriminator column in this subquery
|
||||
* @param selection A filter declaring which rows to return,
|
||||
* formatted as an SQL WHERE clause (excluding the WHERE
|
||||
* itself). Passing null will return all rows for the given
|
||||
* URL.
|
||||
* @param selectionArgs You may include ?s in selection, which
|
||||
* will be replaced by the values from selectionArgs, in order
|
||||
* that they appear in the selection. The values will be bound
|
||||
* as Strings.
|
||||
* @param groupBy A filter declaring how to group rows, formatted
|
||||
* as an SQL GROUP BY clause (excluding the GROUP BY itself).
|
||||
* Passing null will cause the rows to not be grouped.
|
||||
* @param having A filter declare which row groups to include in
|
||||
* the cursor, if row grouping is being used, formatted as an
|
||||
* SQL HAVING clause (excluding the HAVING itself). Passing
|
||||
* null will cause all row groups to be included, and is
|
||||
* required when row grouping is not being used.
|
||||
* @return the resulting SQL SELECT statement
|
||||
*/
|
||||
public String buildUnionSubQuery(
|
||||
String typeDiscriminatorColumn,
|
||||
String[] unionColumns,
|
||||
Set<String> columnsPresentInTable,
|
||||
int computedColumnsOffset,
|
||||
String typeDiscriminatorValue,
|
||||
String selection,
|
||||
String[] selectionArgs,
|
||||
String groupBy,
|
||||
String having) {
|
||||
int unionColumnsCount = unionColumns.length;
|
||||
String[] projectionIn = new String[unionColumnsCount];
|
||||
|
||||
for (int i = 0; i < unionColumnsCount; i++) {
|
||||
String unionColumn = unionColumns[i];
|
||||
|
||||
if (unionColumn.equals(typeDiscriminatorColumn)) {
|
||||
projectionIn[i] = "'" + typeDiscriminatorValue + "' AS "
|
||||
+ typeDiscriminatorColumn;
|
||||
} else if (i <= computedColumnsOffset
|
||||
|| columnsPresentInTable.contains(unionColumn)) {
|
||||
projectionIn[i] = unionColumn;
|
||||
} else {
|
||||
projectionIn[i] = "NULL AS " + unionColumn;
|
||||
}
|
||||
}
|
||||
return buildQuery(
|
||||
projectionIn, selection, selectionArgs, groupBy, having,
|
||||
null /* sortOrder */,
|
||||
null /* limit */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a set of subqueries, all of which are SELECT statements,
|
||||
* construct a query that returns the union of what those
|
||||
* subqueries return.
|
||||
* @param subQueries an array of SQL SELECT statements, all of
|
||||
* which must have the same columns as the same positions in
|
||||
* their results
|
||||
* @param sortOrder How to order the rows, formatted as an SQL
|
||||
* ORDER BY clause (excluding the ORDER BY itself). Passing
|
||||
* null will use the default sort order, which may be unordered.
|
||||
* @param limit The limit clause, which applies to the entire union result set
|
||||
*
|
||||
* @return the resulting SQL SELECT statement
|
||||
*/
|
||||
public String buildUnionQuery(String[] subQueries, String sortOrder, String limit) {
|
||||
StringBuilder query = new StringBuilder(128);
|
||||
int subQueryCount = subQueries.length;
|
||||
String unionOperator = mDistinct ? " UNION " : " UNION ALL ";
|
||||
|
||||
for (int i = 0; i < subQueryCount; i++) {
|
||||
if (i > 0) {
|
||||
query.append(unionOperator);
|
||||
}
|
||||
query.append(subQueries[i]);
|
||||
}
|
||||
appendClause(query, " ORDER BY ", sortOrder);
|
||||
appendClause(query, " LIMIT ", limit);
|
||||
return query.toString();
|
||||
}
|
||||
|
||||
private String[] computeProjection(String[] projectionIn) {
|
||||
if (projectionIn != null && projectionIn.length > 0) {
|
||||
if (mProjectionMap != null) {
|
||||
String[] projection = new String[projectionIn.length];
|
||||
int length = projectionIn.length;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
String userColumn = projectionIn[i];
|
||||
String column = mProjectionMap.get(userColumn);
|
||||
|
||||
if (column != null) {
|
||||
projection[i] = column;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!mStrictProjectionMap &&
|
||||
( userColumn.contains(" AS ") || userColumn.contains(" as "))) {
|
||||
/* A column alias already exist */
|
||||
projection[i] = userColumn;
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Invalid column "
|
||||
+ projectionIn[i]);
|
||||
}
|
||||
return projection;
|
||||
} else {
|
||||
return projectionIn;
|
||||
}
|
||||
} else if (mProjectionMap != null) {
|
||||
// Return all columns in projection map.
|
||||
Set<Entry<String, String>> entrySet = mProjectionMap.entrySet();
|
||||
String[] projection = new String[entrySet.size()];
|
||||
Iterator<Entry<String, String>> entryIter = entrySet.iterator();
|
||||
int i = 0;
|
||||
|
||||
while (entryIter.hasNext()) {
|
||||
Entry<String, String> entry = entryIter.next();
|
||||
|
||||
// Don't include the _count column when people ask for no projection.
|
||||
if (entry.getKey().equals(BaseColumns._COUNT)) {
|
||||
continue;
|
||||
}
|
||||
projection[i++] = entry.getValue();
|
||||
}
|
||||
return projection;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package net.sqlcipher.database;
|
||||
|
||||
public class SQLiteQueryStats {
|
||||
long totalQueryResultSize = 0L;
|
||||
long largestIndividualRowSize = 0L;
|
||||
|
||||
public SQLiteQueryStats(long totalQueryResultSize,
|
||||
long largestIndividualRowSize) {
|
||||
this.totalQueryResultSize = totalQueryResultSize;
|
||||
this.largestIndividualRowSize = largestIndividualRowSize;
|
||||
}
|
||||
|
||||
public long getTotalQueryResultSize(){
|
||||
return totalQueryResultSize;
|
||||
}
|
||||
|
||||
public long getLargestIndividualRowSize(){
|
||||
return largestIndividualRowSize;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
|
||||
import android.os.SystemClock;
|
||||
import androidx.sqlite.db.SupportSQLiteStatement;
|
||||
|
||||
/**
|
||||
* A pre-compiled statement against a {@link SQLiteDatabase} that can be reused.
|
||||
* The statement cannot return multiple rows, but 1x1 result sets are allowed.
|
||||
* Don't use SQLiteStatement constructor directly, please use
|
||||
* {@link SQLiteDatabase#compileStatement(String)}
|
||||
*
|
||||
* SQLiteStatement is not internally synchronized so code using a SQLiteStatement from multiple
|
||||
* threads should perform its own synchronization when using the SQLiteStatement.
|
||||
*/
|
||||
public class SQLiteStatement extends SQLiteProgram implements
|
||||
SupportSQLiteStatement
|
||||
{
|
||||
/**
|
||||
* Don't use SQLiteStatement constructor directly, please use
|
||||
* {@link SQLiteDatabase#compileStatement(String)}
|
||||
* @param db
|
||||
* @param sql
|
||||
*/
|
||||
/* package */ SQLiteStatement(SQLiteDatabase db, String sql) {
|
||||
super(db, sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute this SQL statement, if it is not a query. For example,
|
||||
* CREATE TABLE, DELTE, INSERT, etc.
|
||||
*
|
||||
* @throws android.database.SQLException If the SQL string is invalid for
|
||||
* some reason
|
||||
*/
|
||||
@Override
|
||||
public void execute() {
|
||||
if (!mDatabase.isOpen()) {
|
||||
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
|
||||
}
|
||||
long timeStart = SystemClock.uptimeMillis();
|
||||
mDatabase.lock();
|
||||
|
||||
acquireReference();
|
||||
try {
|
||||
native_execute();
|
||||
} finally {
|
||||
releaseReference();
|
||||
mDatabase.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute this SQL statement and return the ID of the row inserted due to this call.
|
||||
* The SQL statement should be an INSERT for this to be a useful call.
|
||||
*
|
||||
* @return the row ID of the last row inserted, if this insert is successful. -1 otherwise.
|
||||
*
|
||||
* @throws android.database.SQLException If the SQL string is invalid for
|
||||
* some reason
|
||||
*/
|
||||
@Override
|
||||
public long executeInsert() {
|
||||
if (!mDatabase.isOpen()) {
|
||||
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
|
||||
}
|
||||
long timeStart = SystemClock.uptimeMillis();
|
||||
mDatabase.lock();
|
||||
|
||||
acquireReference();
|
||||
try {
|
||||
native_execute();
|
||||
return (mDatabase.lastChangeCount() > 0) ? mDatabase.lastInsertRow() : -1;
|
||||
} finally {
|
||||
releaseReference();
|
||||
mDatabase.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int executeUpdateDelete() {
|
||||
if (!mDatabase.isOpen()) {
|
||||
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
|
||||
}
|
||||
long timeStart = SystemClock.uptimeMillis();
|
||||
mDatabase.lock();
|
||||
|
||||
acquireReference();
|
||||
try {
|
||||
native_execute();
|
||||
return mDatabase.lastChangeCount();
|
||||
} finally {
|
||||
releaseReference();
|
||||
mDatabase.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a statement that returns a 1 by 1 table with a numeric value.
|
||||
* For example, SELECT COUNT(*) FROM table;
|
||||
*
|
||||
* @return The result of the query.
|
||||
*
|
||||
* @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
|
||||
*/
|
||||
@Override
|
||||
public long simpleQueryForLong() {
|
||||
if (!mDatabase.isOpen()) {
|
||||
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
|
||||
}
|
||||
long timeStart = SystemClock.uptimeMillis();
|
||||
mDatabase.lock();
|
||||
|
||||
acquireReference();
|
||||
try {
|
||||
long retValue = native_1x1_long();
|
||||
return retValue;
|
||||
} finally {
|
||||
releaseReference();
|
||||
mDatabase.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a statement that returns a 1 by 1 table with a text value.
|
||||
* For example, SELECT COUNT(*) FROM table;
|
||||
*
|
||||
* @return The result of the query.
|
||||
*
|
||||
* @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
|
||||
*/
|
||||
@Override
|
||||
public String simpleQueryForString() {
|
||||
if (!mDatabase.isOpen()) {
|
||||
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
|
||||
}
|
||||
long timeStart = SystemClock.uptimeMillis();
|
||||
mDatabase.lock();
|
||||
|
||||
acquireReference();
|
||||
try {
|
||||
String retValue = native_1x1_string();
|
||||
return retValue;
|
||||
} finally {
|
||||
releaseReference();
|
||||
mDatabase.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private final native void native_execute();
|
||||
private final native long native_1x1_long();
|
||||
private final native String native_1x1_string();
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
|
||||
/**
|
||||
* A listener for transaction events.
|
||||
*/
|
||||
public interface SQLiteTransactionListener {
|
||||
/**
|
||||
* Called immediately after the transaction begins.
|
||||
*/
|
||||
void onBegin();
|
||||
|
||||
/**
|
||||
* Called immediately before commiting the transaction.
|
||||
*/
|
||||
void onCommit();
|
||||
|
||||
/**
|
||||
* Called if the transaction is about to be rolled back.
|
||||
*/
|
||||
void onRollback();
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright (C) 2008 Esmertec AG.
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
|
||||
import net.sqlcipher.*;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
|
||||
public final class SqliteWrapper {
|
||||
private static final String TAG = "SqliteWrapper";
|
||||
private static final String SQLITE_EXCEPTION_DETAIL_MESSAGE
|
||||
= "unable to open database file";
|
||||
|
||||
private SqliteWrapper() {
|
||||
// Forbidden being instantiated.
|
||||
}
|
||||
|
||||
// FIXME: need to optimize this method.
|
||||
private static boolean isLowMemory(SQLiteException e) {
|
||||
return e.getMessage().equals(SQLITE_EXCEPTION_DETAIL_MESSAGE);
|
||||
}
|
||||
|
||||
public static void checkSQLiteException(Context context, SQLiteException e) {
|
||||
if (isLowMemory(e)) {
|
||||
Toast.makeText(context, e.getMessage(),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static Cursor query(Context context, ContentResolver resolver, Uri uri,
|
||||
String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
||||
try {
|
||||
return (Cursor) resolver.query(uri, projection, selection, selectionArgs, sortOrder);
|
||||
} catch (SQLiteException e) {
|
||||
Log.e(TAG, "Catch a SQLiteException when query: ", e);
|
||||
checkSQLiteException(context, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean requery(Context context, android.database.Cursor cursor) {
|
||||
try {
|
||||
return cursor.requery();
|
||||
} catch (SQLiteException e) {
|
||||
Log.e(TAG, "Catch a SQLiteException when requery: ", e);
|
||||
checkSQLiteException(context, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public static int update(Context context, ContentResolver resolver, Uri uri,
|
||||
ContentValues values, String where, String[] selectionArgs) {
|
||||
try {
|
||||
return resolver.update(uri, values, where, selectionArgs);
|
||||
} catch (SQLiteException e) {
|
||||
Log.e(TAG, "Catch a SQLiteException when update: ", e);
|
||||
checkSQLiteException(context, e);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public static int delete(Context context, ContentResolver resolver, Uri uri,
|
||||
String where, String[] selectionArgs) {
|
||||
try {
|
||||
return resolver.delete(uri, where, selectionArgs);
|
||||
} catch (SQLiteException e) {
|
||||
Log.e(TAG, "Catch a SQLiteException when delete: ", e);
|
||||
checkSQLiteException(context, e);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public static Uri insert(Context context, ContentResolver resolver,
|
||||
Uri uri, ContentValues values) {
|
||||
try {
|
||||
return resolver.insert(uri, values);
|
||||
} catch (SQLiteException e) {
|
||||
Log.e(TAG, "Catch a SQLiteException when insert: ", e);
|
||||
checkSQLiteException(context, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (C) 2019 Mark L. Murphy
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
|
||||
import androidx.sqlite.db.SupportSQLiteOpenHelper;
|
||||
|
||||
public class SupportFactory implements SupportSQLiteOpenHelper.Factory {
|
||||
private final byte[] passphrase;
|
||||
private final SQLiteDatabaseHook hook;
|
||||
|
||||
public SupportFactory(byte[] passphrase) {
|
||||
this(passphrase, (SQLiteDatabaseHook)null);
|
||||
}
|
||||
|
||||
public SupportFactory(byte[] passphrase, SQLiteDatabaseHook hook) {
|
||||
this.passphrase = passphrase;
|
||||
this.hook = hook;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SupportSQLiteOpenHelper create(SupportSQLiteOpenHelper.Configuration configuration) {
|
||||
return new SupportHelper(configuration, passphrase, hook);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (C) 2019 Mark L. Murphy
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sqlcipher.database;
|
||||
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase;
|
||||
import androidx.sqlite.db.SupportSQLiteOpenHelper;
|
||||
|
||||
public class SupportHelper implements SupportSQLiteOpenHelper {
|
||||
private SQLiteOpenHelper standardHelper;
|
||||
private byte[] passphrase;
|
||||
|
||||
SupportHelper(final SupportSQLiteOpenHelper.Configuration configuration,
|
||||
byte[] passphrase, final SQLiteDatabaseHook hook) {
|
||||
SQLiteDatabase.loadLibs(configuration.context);
|
||||
this.passphrase = passphrase;
|
||||
|
||||
standardHelper =
|
||||
new SQLiteOpenHelper(configuration.context, configuration.name,
|
||||
null, configuration.callback.version, hook) {
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
configuration.callback.onCreate(db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion,
|
||||
int newVersion) {
|
||||
configuration.callback.onUpgrade(db, oldVersion,
|
||||
newVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDowngrade(SQLiteDatabase db, int oldVersion,
|
||||
int newVersion) {
|
||||
configuration.callback.onDowngrade(db, oldVersion,
|
||||
newVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(SQLiteDatabase db) {
|
||||
configuration.callback.onOpen(db);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigure(SQLiteDatabase db) {
|
||||
configuration.callback.onConfigure(db);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDatabaseName() {
|
||||
return standardHelper.getDatabaseName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWriteAheadLoggingEnabled(boolean enabled) {
|
||||
standardHelper.setWriteAheadLoggingEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SupportSQLiteDatabase getWritableDatabase() {
|
||||
SQLiteDatabase result = standardHelper.getWritableDatabase(passphrase);
|
||||
if(passphrase != null) {
|
||||
for (int i = 0; i < passphrase.length; i++) {
|
||||
passphrase[i] = (byte)0;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SupportSQLiteDatabase getReadableDatabase() {
|
||||
return getWritableDatabase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
standardHelper.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Contains the SQLCipher database managements classes that an application would use to manage its own private database.
|
||||
*/
|
||||
package net.sqlcipher.database;
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* Contains classes to explore data returned from a SQLCipher database.
|
||||
*/
|
||||
package net.sqlcipher;
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="library_android_database_sqlcipher_author">Zetetic, LLC</string>
|
||||
<string name="library_android_database_sqlcipher_authorWebsite">https://www.zetetic.net/sqlcipher/</string>
|
||||
<string name="library_android_database_sqlcipher_libraryName">SQLCipher for Android</string>
|
||||
<string name="library_android_database_sqlcipher_libraryDescription">Android SQLite API based on SQLCipher</string>
|
||||
<string name="library_android_database_sqlcipher_libraryWebsite">https://www.zetetic.net/sqlcipher/</string>
|
||||
<string name="library_android_database_sqlcipher_libraryVersion">${clientVersionNumber}</string>
|
||||
<string name="library_android_database_sqlcipher_isOpenSource">true</string>
|
||||
<string name="library_android_database_sqlcipher_repositoryLink">https://github.com/sqlcipher/android-database-sqlcipher</string>
|
||||
<string name="library_android_database_sqlcipher_licenseLink">https://www.zetetic.net/sqlcipher/license/</string>
|
||||
</resources>
|
||||
99
BotZone2.8v1 Android/android-database-sqlcipher/build.gradle
Normal file
99
BotZone2.8v1 Android/android-database-sqlcipher/build.gradle
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven {
|
||||
url "https://plugins.gradle.org/m2/"
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath "com.android.tools.build:gradle:3.4.1"
|
||||
classpath "gradle.plugin.org.ec4j.gradle:editorconfig-gradle-plugin:0.0.3"
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
ext {
|
||||
clientVersionNumber = "4.2.0"
|
||||
mavenPackaging = "aar"
|
||||
mavenGroup = "net.zetetic"
|
||||
mavenArtifactId = "android-database-sqlcipher"
|
||||
mavenLocalRepositoryPrefix = "file://"
|
||||
if(project.hasProperty('publishLocal') && publishLocal.toBoolean()){
|
||||
mavenSnapshotRepositoryUrl = "outputs/snapshot"
|
||||
mavenReleaseRepositoryUrl = "outputs/release"
|
||||
} else {
|
||||
mavenLocalRepositoryPrefix = ""
|
||||
mavenSnapshotRepositoryUrl = "https://oss.sonatype.org/content/repositories/snapshots"
|
||||
mavenReleaseRepositoryUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2"
|
||||
}
|
||||
if(project.hasProperty('publishSnapshot') && publishSnapshot.toBoolean()){
|
||||
mavenVersionName = "${clientVersionNumber}-SNAPSHOT"
|
||||
} else {
|
||||
mavenVersionName = "${clientVersionNumber}"
|
||||
}
|
||||
if(project.hasProperty('nexusUsername')){
|
||||
nexusUsername = "${nexusUsername}"
|
||||
}
|
||||
if(project.hasProperty('nexusPassword')){
|
||||
nexusPassword = "${nexusPassword}"
|
||||
}
|
||||
mavenPomDescription = "SQLCipher for Android is a plugin to SQLite that provides full database encryption."
|
||||
mavenPomUrl = "https://www.zetetic.net/sqlcipher"
|
||||
mavenScmUrl = "https://github.com/sqlcipher/android-database-sqlcipher.git"
|
||||
mavenScmConnection = "scm:git:https://github.com/sqlcipher/android-database-sqlcipher.git"
|
||||
mavenScmDeveloperConnection = "scm:git:https://github.com/sqlcipher/android-database-sqlcipher.git"
|
||||
mavenLicenseUrl = "https://www.zetetic.net/sqlcipher/license/"
|
||||
mavenDeveloperName = "Zetetic Support"
|
||||
mavenDeveloperEmail = "support@zetetic.net"
|
||||
mavenDeveloperOrganization = "Zetetic LLC"
|
||||
mavenDeveloperUrl = "https://www.zetetic.net"
|
||||
minimumAndroidSdkVersion = 14
|
||||
minimumAndroid64BitSdkVersion = 21
|
||||
targetAndroidSdkVersion = 26
|
||||
compileAndroidSdkVersion = 26
|
||||
mainProjectName = "android-database-sqlcipher"
|
||||
nativeRootOutputDir = "${projectDir}/${mainProjectName}/src/main"
|
||||
androidNativeRootDir = "${nativeRootOutputDir}/external/android-libs"
|
||||
sqlcipherDir = "${projectDir}/${mainProjectName}/src/main/external/sqlcipher"
|
||||
opensslVersion = "1.1.1b"
|
||||
opensslDir = "${projectDir}/${mainProjectName}/src/main/external/openssl-${opensslVersion}"
|
||||
if(project.hasProperty('debugBuild') && debugBuild.toBoolean()) {
|
||||
otherSqlcipherCFlags = ""
|
||||
ndkBuildType="NDK_DEBUG=1"
|
||||
} else {
|
||||
otherSqlcipherCFlags = "-DLOG_NDEBUG"
|
||||
ndkBuildType="NDK_DEBUG=0"
|
||||
}
|
||||
sqlcipherCFlags = "-DSQLITE_HAS_CODEC " +
|
||||
"-DSQLITE_SOUNDEX " +
|
||||
"-DHAVE_USLEEP=1 " +
|
||||
"-DSQLITE_MAX_VARIABLE_NUMBER=99999 " +
|
||||
"-DSQLITE_TEMP_STORE=3 " +
|
||||
"-DSQLITE_THREADSAFE=1 " +
|
||||
"-DSQLITE_DEFAULT_JOURNAL_SIZE_LIMIT=1048576 " +
|
||||
"-DNDEBUG=1 " +
|
||||
"-DSQLITE_ENABLE_MEMORY_MANAGEMENT=1 " +
|
||||
"-DSQLITE_ENABLE_LOAD_EXTENSION " +
|
||||
"-DSQLITE_ENABLE_COLUMN_METADATA " +
|
||||
"-DSQLITE_ENABLE_UNLOCK_NOTIFY " +
|
||||
"-DSQLITE_ENABLE_RTREE " +
|
||||
"-DSQLITE_ENABLE_STAT3 " +
|
||||
"-DSQLITE_ENABLE_STAT4 " +
|
||||
"-DSQLITE_ENABLE_JSON1 " +
|
||||
"-DSQLITE_ENABLE_FTS3_PARENTHESIS " +
|
||||
"-DSQLITE_ENABLE_FTS4 " +
|
||||
"-DSQLITE_ENABLE_FTS5 " +
|
||||
"-DSQLCIPHER_CRYPTO_OPENSSL " +
|
||||
"-DSQLITE_ENABLE_DBSTAT_VTAB"
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
BIN
BotZone2.8v1 Android/android-database-sqlcipher/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
BotZone2.8v1 Android/android-database-sqlcipher/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
BotZone2.8v1 Android/android-database-sqlcipher/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
BotZone2.8v1 Android/android-database-sqlcipher/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip
|
||||
172
BotZone2.8v1 Android/android-database-sqlcipher/gradlew
vendored
Normal file
172
BotZone2.8v1 Android/android-database-sqlcipher/gradlew
vendored
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
84
BotZone2.8v1 Android/android-database-sqlcipher/gradlew.bat
vendored
Normal file
84
BotZone2.8v1 Android/android-database-sqlcipher/gradlew.bat
vendored
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
|
|
@ -0,0 +1 @@
|
|||
include ':android-database-sqlcipher'
|
||||
6176
BotZone2.8v1 Android/commands.py
Normal file
6176
BotZone2.8v1 Android/commands.py
Normal file
File diff suppressed because it is too large
Load diff
121
BotZone2.8v1 Android/config.py
Normal file
121
BotZone2.8v1 Android/config.py
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
from orator import DatabaseManager, Model
|
||||
|
||||
AdId = None
|
||||
UniqueId = None
|
||||
identifier = None
|
||||
access_token = None
|
||||
secret = None
|
||||
client = 'japan'
|
||||
platform = 'android'
|
||||
|
||||
deck = 1
|
||||
allow_stamina_refill = True
|
||||
|
||||
|
||||
### Database Config
|
||||
jp_config = {'mysql': {'driver': 'sqlite', 'database': 'jp.db'}}
|
||||
glb_config = {'mysql': {'driver': 'sqlite', 'database': 'glb.db'}}
|
||||
db_glb = DatabaseManager(glb_config)
|
||||
db_jp = DatabaseManager(jp_config)
|
||||
Model.set_connection_resolver(db_glb)
|
||||
|
||||
class LeaderSkills(Model):
|
||||
|
||||
__table__ = 'leader_skills'
|
||||
|
||||
class LinkSkills(Model):
|
||||
|
||||
__table__ = 'link_skills'
|
||||
|
||||
class AreaTabs(Model):
|
||||
|
||||
__table__ = 'area_tabs'
|
||||
|
||||
class CardSpecials(Model):
|
||||
|
||||
__table__ = 'card_specials'
|
||||
|
||||
class Passives(Model):
|
||||
|
||||
__table__ = 'passive_skill_sets'
|
||||
|
||||
class Supers(Model):
|
||||
|
||||
__table__ = 'specials'
|
||||
|
||||
class ZBattles(Model):
|
||||
|
||||
__table__ = 'z_battle_stage_views'
|
||||
|
||||
class CardCategories(Model):
|
||||
|
||||
__table__ = 'card_categories'
|
||||
|
||||
class CardCardCategories(Model):
|
||||
|
||||
__table__ = 'card_card_categories'
|
||||
|
||||
class TreasureItems(Model):
|
||||
|
||||
__table__ = 'treasure_items'
|
||||
|
||||
|
||||
class AwakeningItems(Model):
|
||||
|
||||
__table__ = 'awakening_items'
|
||||
|
||||
|
||||
class SupportItems(Model):
|
||||
|
||||
__table__ = 'support_items'
|
||||
|
||||
|
||||
class PotentialItems(Model):
|
||||
|
||||
__table__ = 'potential_items'
|
||||
|
||||
class SpecialItems(Model):
|
||||
|
||||
__table__ = 'special_items'
|
||||
|
||||
|
||||
class TrainingItems(Model):
|
||||
|
||||
__table__ = 'training_items'
|
||||
|
||||
|
||||
class Cards(Model):
|
||||
|
||||
__table__ = 'cards'
|
||||
|
||||
|
||||
class Quests(Model):
|
||||
|
||||
__table__ = 'quests'
|
||||
|
||||
class Ranks(Model):
|
||||
|
||||
__table__ = 'rank_statuses'
|
||||
|
||||
|
||||
class TrainingFields(Model):
|
||||
|
||||
__table__ = 'training_fields'
|
||||
|
||||
|
||||
class Sugoroku(Model):
|
||||
|
||||
__table__ = 'sugoroku_maps'
|
||||
|
||||
|
||||
class Area(Model):
|
||||
|
||||
__table__ = 'areas'
|
||||
|
||||
|
||||
class Medal(Model):
|
||||
|
||||
__table__ = 'awakening_items'
|
||||
|
||||
|
||||
|
||||
33
BotZone2.8v1 Android/decrypt.py
Normal file
33
BotZone2.8v1 Android/decrypt.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# Module : decrypt.py
|
||||
# Author : bssthu
|
||||
# Project : pysqlsimplecipher
|
||||
# Creation date : 2016-06-03
|
||||
# Description :
|
||||
#
|
||||
|
||||
|
||||
import sys
|
||||
from pysqlsimplecipher import decryptor
|
||||
|
||||
|
||||
def usage():
|
||||
print('Usage: python decrypt.py encrypted.db password output.db')
|
||||
|
||||
|
||||
def main():
|
||||
# arguments
|
||||
argv = sys.argv
|
||||
if len(argv) != 4:
|
||||
usage()
|
||||
return
|
||||
filename_in = argv[1]
|
||||
password = bytearray(argv[2].encode('utf8'))
|
||||
filename_out = argv[3]
|
||||
|
||||
decryptor.decrypt_file(filename_in, password, filename_out)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
32
BotZone2.8v1 Android/decryptor.py
Normal file
32
BotZone2.8v1 Android/decryptor.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Module : decrypt.py
|
||||
# Author : bssthu
|
||||
# Project : pysqlsimplecipher
|
||||
# Creation date : 2016-06-03
|
||||
# Description :
|
||||
#
|
||||
|
||||
|
||||
import sys
|
||||
from pysqlsimplecipher import decryptor
|
||||
|
||||
|
||||
def usage():
|
||||
print('Usage: python decrypt.py encrypted.db password output.db')
|
||||
|
||||
|
||||
def main(p = '9bf9c6ed9d537c399a6c4513e92ab24717e1a488381e3338593abd923fc8a13b'):
|
||||
|
||||
password = bytearray(p.encode('utf8'))
|
||||
if p == '9bf9c6ed9d537c399a6c4513e92ab24717e1a488381e3338593abd923fc8a13b':
|
||||
filename_in = 'dataenc_glb.db'
|
||||
filename_out = 'glb.db'
|
||||
else:
|
||||
filename_in = 'dataenc_jp.db'
|
||||
filename_out = 'jp.db'
|
||||
|
||||
decryptor.decrypt_file(filename_in, password, filename_out)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
33
BotZone2.8v1 Android/encrypt.py
Normal file
33
BotZone2.8v1 Android/encrypt.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# Module : encrypt.py
|
||||
# Author : bssthu
|
||||
# Project : pysqlsimplecipher
|
||||
# Creation date : 2016-06-03
|
||||
# Description :
|
||||
#
|
||||
|
||||
|
||||
import sys
|
||||
from pysqlsimplecipher import encryptor
|
||||
|
||||
|
||||
def usage():
|
||||
print('Usage: python encrypt.py plain.db password output.db')
|
||||
|
||||
|
||||
def main():
|
||||
# arguments
|
||||
argv = sys.argv
|
||||
if len(argv) != 4:
|
||||
usage()
|
||||
return
|
||||
filename_in = argv[1]
|
||||
password = bytearray(argv[2].encode('utf8'))
|
||||
filename_out = argv[3]
|
||||
|
||||
encryptor.encrypt_file(filename_in, password, filename_out)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
188
BotZone2.8v1 Android/extra.py
Normal file
188
BotZone2.8v1 Android/extra.py
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
import commands
|
||||
from colorama import init, Fore, Back, Style
|
||||
# Coloroma autoreset
|
||||
init(autoreset=True)
|
||||
|
||||
|
||||
def sbr_new():
|
||||
|
||||
print('---------------------------------')
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Universe Survival Sagaa Category Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710021', 5)
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Super Saiyan 3" Category Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710022', 5)
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Giant Form Category Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710023', 5)
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Transformation Boost Category Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710024', 5)
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Ginyu Force Category Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710025', 5)
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Movie Bosses Category Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710026', 5)
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Pure Saiyans Category Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710027', 5)
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Future Saga Category Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710028', 5)
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Full Power Category Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710029', 5)
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Androids Category Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710030', 5)
|
||||
|
||||
|
||||
def sbr_next():
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Super Class Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710011', 5)
|
||||
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Extreme Class Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710012', 5)
|
||||
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Fusion Category Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710013', 5)
|
||||
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Shadow Dragons Category Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710014', 5)
|
||||
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Peppy Gals Category Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710015', 5)
|
||||
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Hybrid Saiyans Category Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710016', 5)
|
||||
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Resurrected Warriors Category Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710017', 5)
|
||||
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Realm of Gods Category Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710018', 5)
|
||||
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Majin Buu Saga Category Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710019', 5)
|
||||
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Potara Category Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710020', 5)
|
||||
|
||||
print('---------------------------------')
|
||||
|
||||
|
||||
def complete_sbr():
|
||||
print('------------------------------------')
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Super TEQ Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710001', 5)
|
||||
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Extreme TEQ Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710002', 5)
|
||||
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Super INT Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710003', 5)
|
||||
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Extreme INT Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710004', 5)
|
||||
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Super PHY Team')
|
||||
|
||||
commands.complete_stage('710005', 5)
|
||||
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Extreme PHY Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710006', 5)
|
||||
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Super AGL Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710007', 5)
|
||||
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Extreme AGL Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710008', 5)
|
||||
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Super STR Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710009', 5)
|
||||
|
||||
print(Fore.YELLOW + Style.BRIGHT + 'Clear with Extreme STR Team')
|
||||
|
||||
commands.change_team()
|
||||
|
||||
commands.complete_stage('710010', 5)
|
||||
|
||||
|
||||
print('---------------------------------')
|
||||
|
||||
139
BotZone2.8v1 Android/packet.py
Normal file
139
BotZone2.8v1 Android/packet.py
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
### packet.py contains functions critical to sending requests to the server
|
||||
|
||||
import base64
|
||||
import binascii
|
||||
import config
|
||||
from Crypto.Cipher import AES
|
||||
import hashlib
|
||||
import hmac
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import uuid
|
||||
|
||||
# Padding for the input string --not
|
||||
# related to encryption itself.
|
||||
|
||||
BLOCK_SIZE = 16 # Bytes
|
||||
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * chr(BLOCK_SIZE
|
||||
- len(s) % BLOCK_SIZE)
|
||||
unpad = lambda s: s[:-ord(s[len(s) - 1:])]
|
||||
|
||||
####################################################################
|
||||
|
||||
def guid():
|
||||
|
||||
# Generates UniqueID & AdIDcompatible with Bandais servers
|
||||
# Returns dict
|
||||
|
||||
UUID = str(uuid.uuid4())
|
||||
UniqueId = str(uuid.uuid4()) + ':' + UUID[0:8]
|
||||
return dict(AdId=str(uuid.uuid4()), UniqueId=UniqueId)
|
||||
|
||||
####################################################################
|
||||
|
||||
def mac(method,action):
|
||||
|
||||
# Creates Mac Authentication header string used when sending requests
|
||||
# returns string
|
||||
|
||||
ts = str(int(round(time.time(), 0)))
|
||||
nonce = ts + ':' + config.AdId
|
||||
if config.client == 'global':
|
||||
value = ts + '\n' + nonce + '\n' + method + '\n' + action + '\n' \
|
||||
+ 'ishin-global.aktsk.com' + '\n' + '3001' + '''
|
||||
|
||||
'''
|
||||
else:
|
||||
value = ts + '\n' + nonce + '\n' + method + '\n' + action + '\n' \
|
||||
+ 'ishin-production.aktsk.jp' + '\n' + '3001' + '''
|
||||
|
||||
'''
|
||||
|
||||
|
||||
hmac_hex_bin = hmac.new(config.secret.encode('utf-8'), value.encode('utf-8'
|
||||
), hashlib.sha256).digest()
|
||||
mac = base64.b64encode(hmac_hex_bin).decode()
|
||||
final = 'MAC ' + 'id=' + '"' + config.access_token + '"' + ' nonce=' + '"' \
|
||||
+ nonce + '"' + ' ts=' + '"' + ts + '"' + ' mac=' + '"' + mac \
|
||||
+ '"'
|
||||
return final
|
||||
|
||||
####################################################################
|
||||
# ================================================================
|
||||
# get_key_and_iv
|
||||
# ================================================================
|
||||
|
||||
def get_key_and_iv(
|
||||
password,
|
||||
salt,
|
||||
klen=32,
|
||||
ilen=16,
|
||||
msgdgst='md5',
|
||||
):
|
||||
'''
|
||||
Derive the key and the IV from the given password and salt.
|
||||
|
||||
This is a niftier implementation than my direct transliteration of
|
||||
the C++ code although I modified to support different digests.
|
||||
|
||||
CITATION: http://stackoverflow.com/questions/13907841/implement-openssl-aes-encryption-in-python
|
||||
|
||||
@param password The password to use as the seed.
|
||||
@param salt The salt.
|
||||
@param klen The key length.
|
||||
@param ilen The initialization vector length.
|
||||
@param msgdgst The message digest algorithm to use.
|
||||
'''
|
||||
|
||||
# equivalent to:
|
||||
# from hashlib import <mdi> as mdf
|
||||
# from hashlib import md5 as mdf
|
||||
# from hashlib import sha512 as mdf
|
||||
|
||||
mdf = getattr(__import__('hashlib', fromlist=[msgdgst]), msgdgst)
|
||||
password = password.encode('ascii', 'ignore') # convert to ASCII
|
||||
|
||||
try:
|
||||
maxlen = klen + ilen
|
||||
keyiv = mdf(password + salt).digest()
|
||||
tmp = [keyiv]
|
||||
while len(tmp) < maxlen:
|
||||
tmp.append(mdf(tmp[-1] + password + salt).digest())
|
||||
keyiv += tmp[-1] # append the last byte
|
||||
key = keyiv[:klen]
|
||||
iv = keyiv[klen:klen + ilen]
|
||||
return (key, iv)
|
||||
except UnicodeDecodeError:
|
||||
return (None, None)
|
||||
|
||||
|
||||
####################################################################
|
||||
def encrypt_sign(data):
|
||||
data = pad(data)
|
||||
key1 = str.encode(data)
|
||||
password = \
|
||||
'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzJ9JaHioVi6rr0TAfr6j'
|
||||
salt = os.urandom(8)
|
||||
(key, iv) = get_key_and_iv(password, salt, klen=32, ilen=16,
|
||||
msgdgst='md5')
|
||||
cipher = AES.new(key, AES.MODE_CBC, iv)
|
||||
a = cipher.encrypt(key1)
|
||||
a = salt + a
|
||||
return base64.b64encode(a).decode()
|
||||
|
||||
|
||||
####################################################################
|
||||
def decrypt_sign(sign):
|
||||
buffer = base64.b64decode(sign)
|
||||
buffer_encoded = base64.b64encode(buffer)
|
||||
password = \
|
||||
'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzJ9JaHioVi6rr0TAfr6j'
|
||||
salt = buffer[0:8]
|
||||
(key, iv) = get_key_and_iv(password, salt, klen=32, ilen=16,
|
||||
msgdgst='md5')
|
||||
data = buffer[8:len(buffer)]
|
||||
cipher = AES.new(key, AES.MODE_CBC, iv)
|
||||
a = unpad(cipher.decrypt(data)).decode('utf8')
|
||||
return json.loads(a)
|
||||
####################################################################
|
||||
0
BotZone2.8v1 Android/pysqlsimplecipher/__init__.py
Normal file
0
BotZone2.8v1 Android/pysqlsimplecipher/__init__.py
Normal file
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue