Building an AAOS emulator with GPS generator, GApps and built-in x86/ARM support

이제야 이것을 올립니다. . 사실, 이전부터 올리려고 했는데 여러 이유로 지연이 되었습니다. 현 상태에서 마무리를 하고 올리려고 합니다.

AAOS(Android Automotive OS) Emulator 이고, GPS 위치가 수도권 순환도로 판교, 중동을 왕복 하도록 설정 되어 있습니다.

시작 했던 이유는,

이전에 tmap을 OEM GPS(apple pie – ApplePie Carplay Android Ai Box Apple pie Car Smart Box) 가 가능하게 수정 했었습니다.
참고: tmap 9.18 업데이트 – flywithu
하지만 tmap을 제작하는 티모빌리티에서 Email과 집으로 우편물이 왔습니다. 리버싱한거는 공유하지 말라고. Email만 보내도 되었을텐데, 우편물은 좀…

테스트를 효율성을 위해서 emulator 작업 중이었는데, 이것도 중단하고 tmap도 다 삭제 했었습니다. 그러나 emulator는 필요한 분도 있을것 같아 이번에 정리해서 올립니다.

기능은
X86 AAOS(AOSP) emulator (AOSP11): 설정에 따라 phone 및 AAOS 모두 사용 가능 합니다. docker/config 를 aosp_car_x86_64-user 또는 sdk_phone_x86_64-user 로 수정하면 됩니다.
arm -> x86 인터프리터 => 이 라이브러리로 arm전용 앱인 tmap도 설치 및 사용 가능.
수도권순환도로를 왔다 갔다 하는 GPS 값
Google Playstore 및 Korean IME
요렇게 있습니다.

참고로 AOSP9용으로도 x86/arm houdini library가 있는데, Tmap이 Android9(API28) 지원한다고는 적혀 있으나, 제가 테스트 했을떄는 API30 (AOSP10)용 API가 사용되는것으로 보였습니다. 실행하다 에러가 나는데 디버깅하다가 그냥 AOSP11로 업그레이드 했습니다.

설치 및 사용 방법은 아래와 같습니다.

Preconditon: Linux, docker, X86 PC, 500GB HDD

  • Build Instruction
  1. sudo apt-get install git-core gnupg flex bison build-essential zip curl zlib1g-dev libc6-dev-i386 libncurses5 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip fontconfig
  2. mkdir -p git/aosp_car
  3. cd git/aosp_car
  4. git clone https://github.com/flywithu/docker.git
  5. Phone용으로 빌드 하고자 할때 – 기본적으로 AAOS로 빌드합니다.
    cat <<EOF>docker/config
    sdk_phone_x86_64-user
    EOF
  6. docker/fly.sh
  7. docker/with.sh
  8. docker/u.sh

fly.sh => 소스 다운로드 및 Patch : 아래 message가 나올떄 까지 재시도 필요 (네트워크 에러로 소스 다운로드가 완료 안되는 경우가 있습니다. )


with.sh => 빌드 . 몇시간 걸림


u.sh => 에물레이터 실행 (tmap은 sdk_phone_x86_64로 설치시는 playstore에서 다운 로드 가능하지만, aosp_car_x86_64로 빌드 했을때는 별도로 apk를 설치 해야 합니다. AAOS지원하는 앱이 별로 없어 보입니다. (Android용 티맵 – 대중교통, 대리운전, 주차, 렌터카, 공항버스 APK 다운로드 (apkpure.com))

테스트
TMAP: playstore 에 로그인 해서 다운로드 하면 됩니다. 목적지는 ‘시흥하늘휴게소’로 하면 됩니다. 다만 운전기록이 남아서 안전운전 점수에 영향을 줄수 있습니다.

GPS: GPS 앱으로 보면 4개중 4개의 GPS가 fix 되었고, 감도는 60으로 나옵니다..

Reference

  1. AOSP build: Build Android  |  Android Open Source Project
  2. X86 Emu: Android Generic (android-generic.github.io)
  3. Gapps: The Open GApps Project

IPTime 공유기로 두지역의 사무실 하나의 네트워크로 연결하기

사무실 두지역을 네트워크로 연결하는 방법은 여러가지가 있습니다.

VPN으로 연결하거나, 클라우드로 연결하기 또는 전용 솔루션 및 라우터 사용하기 입니다. 여기서는 가장 저렴하게 구축할수 있는 IPTime 공유기 두대로 VPN으로 묶어서 사용하는 방법을 설명하려고 합니다.

먼저 공유기의 IP대역은 서로 달라야 합니다. 아래 처럼 공유기 1은 192.168.10.1/24, 공유기2는 192.168.0.1/24 입니다. 그리고 각각이 인터넷으로 연결이 됩니다.

위와 같이 VPN 서버 설정, 또는 Wireguard 서버 설정을 합니다. Wireguard 서버 설정이 속도가 더 빠릅니다. 하단에 각 프로토콜별 속도 비교는 맨 하단을 참고 하세요.

그다음 클라이언트 사용 관리에서 IP 라우팅을 설정해 줍니다.
192.168.0.1 공유기는 목적지 IP를 192.168.10.0/24 로 설정합니다.
반대로 192.168.10.1공유기는 목적지 IP를 192.168.0.0/24로 설정합니다.
이렇게 하면 기존 IP는 공유기를 타고 나가고, 다른 지역의 공유기는 목적지 IP를 타고 나갑니다.

아래와 같이 두 지점이 VPN으로 연결되서, IP 만으로 서로 접속이 됩니다. 두곳 중 어디서든 IP만으로 상대 지역에 접속이 됩니다.

LT2P

LT2P 속도 입니다. 16.3M / 26.7M 입니다.

WIREGUARD

WIREGUARD속도 입니다. 102M/132M

이와 같이 Ping은 1ms 차이나지만, 속도는 3X~4X 차이가 납니다. 그만큼 Wireguard가 속도면에서 훨씬 우수하기 때문에, 가능한 wireguard를 추천합니다. 그러나 속도 보다 안정성이 중요하다면 LT2P가 좋습니다. 아무래도 오래된 프로토콜이기 때문입니다.

wordpress에서 라이브러리와 블로그 링크

wordpress로 블로그를 올리고 미디어 파일을 올립니다. 그런데 미디어파일을 별도로 올리거나 하는 경우 아래와 같이 미디어 파일과 블로그의 글의 링크가 깨지는 경우가 있습니다.

아래의 스크립트를 돌리면 이러한 글들을 미디어파일과 블로그 글을 링크로 연결 해줍니다.

# -*- encoding:utf8 -*-
from curses.ascii import isdigit
from select import select
from turtle import Screen, isvisible
import requests
import random
from configparser import ConfigParser
from selenium import webdriver
from bs4 import BeautifulSoup
import sys
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
import math
from selenium.webdriver.support.ui import Select
import logging
import os
from Screenshot import Screenshot_Clipping
from PIL import Image
from inspect import currentframe, getframeinfo
from urllib.request import Request, urlopen
from datetime import datetime,date,timedelta
from requests.auth import HTTPBasicAuth
import json
import base64, json, re, requests

In [ ]:

username = 'ID'
password = 'PW'
baseurl = "https://flywithu.com/wp-json/wp/v2"

In [ ]:

credentials = username + ':' + password
token = base64.b64encode(credentials.encode())

header = {
'Authorization': 'Basic ' + token.decode('utf-8'),
'Content-Type': 'application/json'}

In [ ]:

page = 1
per_page=30
mymedias=[]
while True:
    data = {
    'per_page': per_page,
    'page': page
    }

    response = requests.get(baseurl+"/media", headers=header,data=json.dumps(data))
    result = response.json()
    item_count = len(result)
    page=page+1
    if(item_count<per_page):
        break
    # print(result)
    mymedias.extend(result)
    print("*",end="")

In [ ]:

page = 1
per_page=30
myposts=[]
while True:
    data = {
    'per_page': per_page,
    'page': page
    }

    response = requests.get(baseurl+"/posts", headers=header,data=json.dumps(data))
    result = response.json()
    item_count = len(result)
    page=page+1

    # print(result)
    myposts.extend(result)
    print("*",end="")
    if(item_count<per_page):
        break
page=1
while True:
    data = {
    'per_page': per_page,
    'page': page
    }

    response = requests.get(baseurl+"/pages", headers=header,data=json.dumps(data))
    result = response.json()
    item_count = len(result)
    page=page+1

    # print(result)
    myposts.extend(result)
    print("*",end="")
    if(item_count<per_page):
        break

In [ ]:

def download_file(url, save_path):
    response = requests.get(url)
    
    if response.status_code == 200:
        with open(save_path, 'wb') as file:
            file.write(response.content)
        print(f"파일 다운로드 완료: {save_path}")
    else:
        print("파일 다운로드 실패")

In [ ]:

def attach_media_to_post(post_id, attachment_id, baseurl, authname,authpass):
    # 첨부 요청

    credentials = authname + ':' + authpass
    token = base64.b64encode(credentials.encode())

    header = {
   'Authorization': 'Basic ' + token.decode('utf-8'),
   'Content-Type': 'application/json'
}



    
    targetpost = f'{baseurl}/media/{attachment_id}'
    data = {'post':post_id}
    response = requests.post(targetpost, headers=header, data=json.dumps(data))



    if response.status_code == 200:
        # 첨부 성공
        print('미디어 파일이 블로그 글에 첨부되었습니다.')
    else:
        # 첨부 실패
        print('미디어 파일 첨부에 실패했습니다.')
        print(response.text)

In [ ]:

from urllib.parse import urlparse

count = 0 
for mymedia in mymedias:
    # myone = os.path.basename()
    myone =  urlparse(mymedia['guid']['rendered']).path
    print(myone)
    for post in myposts:
        content = post['content']['rendered']
        if myone in content:
            print(post['id'],mymedia['id'])
            attach_media_to_post(post['id'],mymedia['id'],baseurl,username,password)

wordpress에서 미사용 미디어 라이브러리 삭제

최근 몇일동안 사이트 사용에 문제가 있었습니다. 예를들어 댓글이 작성이 안되는 문제였습니다. 그 이유는 웹호스팅 디스크 Full 때문이었습니다.

그동안 중복해서 올린 media 라이브러리가 많았는데, 사용하지 않는 미디어파일을 삭제 하는 노트북 파일 입니다.

파일명으로만 검색하기 때문에, 동일한 파일명으로 사용중일때는 있는 것으로 판단합니다. 즉 좀 더 보수적으로 판단 됩니다. 정확하게는 경로명까지 봐서 판단해야 하는데, 이거는 다음에 올린 다음 버젼에서 적용 되었습니다.

ID/PW 부분만 변경해서 사용하면 동작 할것 입니다.

# -*- encoding:utf8 -*-
from curses.ascii import isdigit
from select import select
from turtle import Screen, isvisible
import requests
import random
from configparser import ConfigParser
from selenium import webdriver
from bs4 import BeautifulSoup
import sys
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
import math
from selenium.webdriver.support.ui import Select
import logging
import os
from Screenshot import Screenshot_Clipping
from PIL import Image
from inspect import currentframe, getframeinfo
from urllib.request import Request, urlopen
from datetime import datetime,date,timedelta
from requests.auth import HTTPBasicAuth
import json
import base64, json, re, requests

In [244]:

username = 'ID'
password = 'Password'
baseurl = "https://flywithu.com/wp-json/wp/v2"


In [245]:

credentials = username + ':' + password
token = base64.b64encode(credentials.encode())

header = {
'Authorization': 'Basic ' + token.decode('utf-8'),
'Content-Type': 'application/json'}

In [246]:

page = 1
per_page=30
mymedias=[]
while True:
    data = {
    'per_page': per_page,
    'page': page
    }

    response = requests.get(baseurl+"/media", headers=header,data=json.dumps(data))
    result = response.json()
    item_count = len(result)
    page=page+1
    if(item_count<per_page):
        break
    # print(result)
    mymedias.extend(result)
    print("*",end="")
**********************

In [247]:

page = 1
per_page=30
myposts=[]
while True:
    data = {
    'per_page': per_page,
    'page': page
    }

    response = requests.get(baseurl+"/posts", headers=header,data=json.dumps(data))
    result = response.json()
    item_count = len(result)
    page=page+1

    # print(result)
    myposts.extend(result)
    print("*",end="")
    if(item_count<per_page):
        break
page=1
while True:
    data = {
    'per_page': per_page,
    'page': page
    }

    response = requests.get(baseurl+"/pages", headers=header,data=json.dumps(data))
    result = response.json()
    item_count = len(result)
    page=page+1

    # print(result)
    myposts.extend(result)
    print("*",end="")
    if(item_count<per_page):
        break
****************************************************************

In [248]:

mycontent=""
for post in myposts:
    content = post['content']['rendered']
    mycontent=mycontent+content
    
    

In [249]:

def save_text_to_file(text, file_path):
    with open(file_path, 'w') as file:
        file.write(text)

save_text_to_file(mycontent,"result.txt")

In [250]:

def download_file(url, save_path):
    response = requests.get(url)
    
    if response.status_code == 200:
        with open(save_path, 'wb') as file:
            file.write(response.content)
        print(f"파일 다운로드 완료: {save_path}")
    else:
        print("파일 다운로드 실패")

In [251]:

if 'Myimage-29.png' in mycontent:
    print("OK")

In [252]:

mymedia_hash=[]
count = 0 
for mymedia in mymedias:
    myone = os.path.basename(mymedia['guid']['rendered'])
    if myone not in mycontent:
        print(myone)
        print(mymedia['guid']['rendered'])
        print(mymedia['id'])
        download_file(mymedia['guid']['rendered'],"data/"+myone)
        deleteurl = baseurl+f"/media/{mymedia['id']}"
        print(deleteurl)
        count=count+1

        data = {
    'id': mymedia['id'],
    'force': 1,
    }
        print(data)
        res = requests.delete(deleteurl,headers=header,data=json.dumps(data))
        print(res.status_code)

print(count)
tmpr5ze2f_a.png
https://www.flywithu.com/wp-content/uploads/2023/05/tmpr5ze2f_a.png
6831
파일 다운로드 완료: data/tmpr5ze2f_a.png

GPT to WordPress 파이썬 코드

https://www.flywithu.com/gpt-%ec%82%ac%ec%9a%a9-%ed%9b%84%ea%b8%b0/

이글에서 언급된 파이썬 코드 입니다.

개발 환경은 Kaggle 서버 입니다. (www.kaggle.com)
여기의 장점은 스케줄링이 되서, 매일 한번씩 코드가 자동 실행 됩니다. 그것도 무료로!
스케줄링 설정은 아래와 작성한 파이썬에 대해서 설정해줍니다. 그러면 UTC 0 (9:00AM KST)에 실행이 됩니다.

사실 코드가 별로 길지도 않아서..
먼저 OpenAI의 API Key를 가져 옵니다. (https://platform.openai.com/account/api-keys)

그리고 wordpress의 Key도 가져 옵니다.

https://github.com/metaqsol/card/blob/main/gpt-to-wordpress.ipynb

코드는 위와 같습니다.

GPT 사용 후기

아무말 대잔치로 GPT를 통해서 Post를 해보았습니다. 몇번의 시행착오를 겪었는데,
처음에는 네이버 기사를 가져와서 글을 쓰게 했는데.. 정말 정말 아무말 대잔치.
GPT는 2021년까지 밖에 모르니, 최신 뉴스를 가져와서 글을 쓰게 하니.

그래서 그 다음으로 한거는 이미 알고 있고, 그리고 재미도 있을만한 옛날 이야기를 작성하게 했습니다.

https://www.flywithu.com/category/chatgpt/

아래와 같이 글을 작성해주는데, GPT도 알고 있던 시대이니, 본문도 맞지 않을까 싶습니다.
그러나 내용의 깊이는 부족하지 않나 싶습니다. 뭔가 프롬프트를 개선해야 할 것 같습니다.

아무말 대잔치의 결과를 보자면.. 포스팅 한개에 0.04$정도 비용이 발생하고, 트래픽으로 보면 아래과 같이 소폭 상승은 했습니다.

그러나 이렇게 생성된 Contents가 개인적으로 별로 유용하지 않습니다. 내가 저 내용을 알아서 뭐할꺼며.. 아마 Wiki를 보는게 더 정확하고 내용이 깊을것 같습니다. 그러나 내용을 표현해주는 Dali의 그림을 좋아 보입니다. 추후에도 Dali는 좀 사용하것 같습니다.

개인적으로 ChatGPT를 최근에 활용했던 것은
글 작성 -> GPT를 통해서 내용을 좀 더 풍부하게, 그리고 맞춤법등의 보정 -> 글 수정 및 퇴고
의 과정에서 사용했습니다. 그러나 내용을 풍부하게 하면서, 가짜 근거를 만들고 붙여줘서, 마지막 단계에서는 정말 잘 읽어봐야 했습니다.

종합적으로 보면, 내가 정답을 검증 할수 있거나(Code), 정답이 없는 것 (그림 같은거)는 확실히 GPT/DALI가 좋습니다. 그러나 내가 모르는 것에 GPT를 이용했다가는 낭패를 겪을수 있습니다.

하도 GPT를 이용한 자동 포스팅이 유행이라, 공짜 credit으로 한번 해보았으나, 향후에는 보정용으로만 활용하지, 완전히 작성용으로는 사용 안 할것 같습니다. 여기에 사용한 코드 및 간단한 메뉴얼은 별도 포스팅으로 남깁니다.

By ChatGPT/DALI – 기원전 206년 – 진나라가 멸망한다. 항우가 신안대학살을 벌여 진의 군대 20만을 생매장하고, 18명의 제후를 왕에 임명한다. 유방이 진에서 독립한 조타를 남월왕에 임명한다.

연기원전 206년, 진나라는 신안대학살을 당하여 멸망하게 되었다. 항우가 대학살을 벌여 진의 군대는 20만의 사람들이 생매장되었고, 18명의 제후는 왕에 임명되었다. 이때 유방이라는 이름의 사람이 나타났고, 이 사람은 진에서 독립한 조타를 남월왕에 임명하기로 결정했다.

그날, 진나라는 수많은 사람들의 눈물과 슬픔에 잠겼다. 그들은 이미 자기들의 나라가 사라지고 있는 것을 알고 있었다. 하지만 그들은 이미 희망이 없는 상황에 처해 있었기 때문에 더 이상 반발할 수 없었다. 그들의 시신은 역사의 흐름에 따라 진나라에서 멸망하고 조타가 왕위를 차지하는 것을 인정해야 했다.

진나라는 역사를 바꾸는 데 실패하였지만, 이 사건은 더 나은 미래를 위해 노력하는 사람들에게 기쁨과 희망을 주었다. 이는 역사가 바뀌지 않아도 나쁘지 않은 결과를 낳을 수 있다는 것을 인정해주는 방법이었다. 이는 우리가 사랑과 희망으로 역사를 바꿀 수 있다는 것을 보여줬다.

By ChatGPT/DALI – 기원전 209년 – 진의 가혹한 법률에 반발해 진승·오광의 난이 발생하고, 이듬해에 제압된다.

209년 전 중국 진주에서 큰 난이 발생했다. 이 난은 진의 가혹한 법률에 반발하는 진승과 오광의 반란으로 시작되었다. 반란군은 진의 법률을 개혁하고 자유를 실현하기 위해 노력했지만, 이듬해에 이르러 대규모의 제압이 있었다.

진주의 반란에 따라 진의 법률이 개혁되었고, 사람들은 자유를 실현할 수 있게 되었다. 그러나 적군의 강력한 공격으로 인해 반란군은 이듬해에 제압을 받았다. 반란군은 높은 희생을 끼치며 진주를 수호하려고 노력했지만, 적군의 강력한 공격으로 인해 제압을 받게 되었다.

209년 전 중국 진주에서 벌어진 진승과 오광의 난은 진의 가혹한 법률에 반발하는 반란으로 시작되었으며, 이듬해에 적군의 강력한 공격으로 인해 반란군은 제압을 받게 되었다. 이 난은 진의 법률을 개혁하고 사람들이 자유를 실현할 수 있게 하기 위한 노력이었지만, 높은 희생을 끼치며 반란군은 제압을 받게 되었다.

By ChatGPT/DALI – 기원전 210년 – 진시황이 천하를 순행하다 급사하고, 호해가 제위를 찬탈한다.

진시황이 천하를 순행하는 것은, 역사상 유명한 일로 기록되어 있다. 그는 모든 나라들을 방문하며 다양한 사건들을 다루는 데 성공하였다. 또한, 그는 이들 나라들의 사회적 문제들을 해결하고 정치적 건전성을 도모하는 데 역적인 역할을 하였다.

그러나 그의 순행은 호해가 제위를 찬탈하는 것과는 연관이 있다. 호해는 진시황의 여행 중 방문한 중국 나라의 왕족들과 분리되는 것을 요구하였고, 이로 인해 중국 사회에 갈등이 일어났다. 호해는 진시황이 중국의 왕족들과 같이 순행하는 것을 반대하여, 제위를 찬탈하는 것을 시도하였다.

그러나 진시황은 호해가 제위를 찬탈하는 것에 대해 반대하고, 다양한 방법으로 그를 억누르려고 했다. 그는 중국 정치권력들에게 호해가 제위를 찬탈하는 것이 중국의 정치적 건전성을 위협하는 것이라고 설명하며, 그를 막기 위한 노력을 하였다.

그리고 진시황의 노력은 성공하였다. 그는 호해가 제위를 찬탈하는 것을 막아냈고, 중국 사회는 정치적 건전성을 유지할 수 있었다. 따라서, 진시황이 천하를 순행하는 것은 역사상 중요한 사건으로 기록되어 있다.

By ChatGPT/DALI – 기원전 212년 – 불로초 탐색 실패를 비판하자 진시황이 함양에서 불온 사상가 460여 명을 생매장한다.

진시황이 함양에서 불온 사상가 460여 명을 생매장하는 것은 기원전 212년 불로초가 함양을 탐색하는 데 실패한 것을 비판하는 의미를 가지고 있습니다. 불로초는 함양에 도착하기 전에는 불온 사상가들과 접촉하지 못했으며, 불로초가 함양에 도착하고 나서 불온 사상가들을 생매장하기 시작하였습니다. 이는 불로초가 함양에 도착한 후 불온 사상가들과의 접촉을 통해 생각의 차이를 발견하고 이를 비판하고자 하는 의미를 가지고 있습니다. 이는 진시황이 불로초가 함양을 탐색하는 데 실패한 것을 비판하는 것이라고 볼 수 있습니다.