<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Hjortsberg's notes</title>
    <description>Notes from stuff I've been working or thinking on and felt I want to write down. Mostly for my own memory but also to share with those who are interested.
</description>
    <link>http://hjortsberg.org/notes/</link>
    <atom:link href="http://hjortsberg.org/notes/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Wed, 28 Dec 2022 19:30:28 +0100</pubDate>
    <lastBuildDate>Wed, 28 Dec 2022 19:30:28 +0100</lastBuildDate>
    <generator>Jekyll v3.5.1</generator>
    
      <item>
        <title>Building Nginx modules using CMake</title>
        <description>&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;Modules in Nginx can be either dynamic or static. A dynamic module is a shared
object that is loaded by Nginx at runtime and Nginx need to be instructed in the
configuration to load the module.&lt;/p&gt;

&lt;p&gt;A static module is compiled into the Nginx binary and, hence, does not need to
be specified in Nginx configuration.&lt;/p&gt;

&lt;p&gt;With dynamic modules you can usually use the Nginx version provided by the
package manager of your operating system so you only build the module and update
the configuration.&lt;/p&gt;

&lt;p&gt;With a static module you will need to build your own Nginx binary.&lt;/p&gt;

&lt;p&gt;This note will describe how to use &lt;a href=&quot;https://cmake.org/&quot;&gt;CMake&lt;/a&gt; to build Nginx
modules. This &lt;a href=&quot;https://github.com/perusio/nginx-hello-world-module&quot;&gt;hello world nginx module&lt;/a&gt;
has been used for testing.&lt;/p&gt;

&lt;h2 id=&quot;determine-nginx-build-options&quot;&gt;Determine Nginx build options&lt;/h2&gt;

&lt;p&gt;When building a dynamically loadable module one will need the Nginx source code.
The Nginx source code used when building the module need to have the same
version as the Nginx binary where the module should be loaded.&lt;/p&gt;

&lt;p&gt;Check the version and configure arguments using command (output is an example
from Nginx in homebrew for OS X):&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ nginx -V
nginx version: nginx/1.23.2
built by clang 14.0.0 (clang-1400.0.29.202)
built with OpenSSL 1.1.1q  5 Jul 2022 (running with OpenSSL 1.1.1s  1 Nov 2022)
TLS SNI support enabled
configure arguments: --prefix=/opt/homebrew/Cellar/nginx/1.23.2 --sbin-path=/opt/homebrew/Cellar/nginx/1.23.2/bin/nginx --with-cc-opt='-I/opt/homebrew/opt/pcre2/include -I/opt/homebrew/opt/openssl@1.1/include' --with-ld-opt='-L/opt/homebrew/opt/pcre2/lib -L/opt/homebrew/opt/openssl@1.1/lib' --conf-path=/opt/homebrew/etc/nginx/nginx.conf --pid-path=/opt/homebrew/var/run/nginx.pid --lock-path=/opt/homebrew/var/run/nginx.lock --http-client-body-temp-path=/opt/homebrew/var/run/nginx/client_body_temp --http-proxy-temp-path=/opt/homebrew/var/run/nginx/proxy_temp --http-fastcgi-temp-path=/opt/homebrew/var/run/nginx/fastcgi_temp --http-uwsgi-temp-path=/opt/homebrew/var/run/nginx/uwsgi_temp --http-scgi-temp-path=/opt/homebrew/var/run/nginx/scgi_temp --http-log-path=/opt/homebrew/var/log/nginx/access.log --error-log-path=/opt/homebrew/var/log/nginx/error.log --with-compat --with-debug --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_degradation_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-ipv6 --with-mail --with-mail_ssl_module --with-pcre --with-pcre-jit --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Make sure that the &lt;code class=&quot;highlighter-rouge&quot;&gt;--with-compat&lt;/code&gt; options is there. If not, you may need to
use the exact same config options when building the module.&lt;/p&gt;

&lt;h2 id=&quot;build-a-dynamic-module&quot;&gt;Build a dynamic module&lt;/h2&gt;

&lt;p&gt;Using a CMake external project is convenient because then source code can be
downloaded, configured and built using any command.&lt;/p&gt;

&lt;p&gt;Create a &lt;code class=&quot;highlighter-rouge&quot;&gt;CMakeLists.txt&lt;/code&gt; file in the module directory and put mandatory
directives in the top of the file&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cmake_minimum_required(VERSION 3.24)
project(nginx-module)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;then create an external project for Nginx like this:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;include(ExternalProject)

ExternalProject_Add(
    nginx
    DOWNLOAD_COMMAND URL https://nginx.org/download/nginx-1.23.2.tar.gz
    CONFIGURE_COMMAND cd ${CMAKE_CURRENT_BINARY_DIR}/nginx-prefix/src/nginx &amp;amp;&amp;amp; ./configure --add-dynamic-module=${CMAKE_BINARY_DIR}/../ --with-compat
    BUILD_COMMAND make -C ${CMAKE_CURRENT_BINARY_DIR}/nginx-prefix/src/nginx modules
    INSTALL_COMMAND &quot;&quot;
)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;As seen the configure command uses &lt;code class=&quot;highlighter-rouge&quot;&gt;--add-dynamic-module&lt;/code&gt; to point out the
directory of the module. When using CMake a separate subdirectory is used for
doing the build in the nginx module directory, that is why we use
&lt;code class=&quot;highlighter-rouge&quot;&gt;${CMAKE_BINARY_DIR}/..&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Make sure to use the same version of Nginx as where you will load the module. If
not, Nginx will fail to start and show an error when loading the module. This is
an example when Nginx 1.23.2 is loading a module build usinh Nginx 1.18 source
code:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;nginx: [emerg] module &quot;/opt/homebrew/Cellar/nginx/1.23.2/modules/ngx_http_hello_world_module.so&quot; version 1018000 instead of 1023002 in /opt/homebrew/etc/nginx/nginx.conf:11
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;You can also add a custom command to copy the module shared object into a
build output directory to the &lt;code class=&quot;highlighter-rouge&quot;&gt;CMakeLists.txt&lt;/code&gt;-file:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;add_custom_command(
     TARGET nginx
     COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_SOURCE_DIR}/output
     COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/nginx-prefix/src/nginx/objs/*.so ${CMAKE_SOURCE_DIR}/output/
     COMMENT &quot;Copy nginx module to ${CMAKE_SOURCE_DIR}/output/&quot;
 )
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Then build the module&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cmake -B build
$ cmake --build build --parallel 8
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The module will be found in the &lt;code class=&quot;highlighter-rouge&quot;&gt;output&lt;/code&gt; directory.&lt;/p&gt;

&lt;h3 id=&quot;load-the-dynamic-module&quot;&gt;Load the dynamic module&lt;/h3&gt;

&lt;p&gt;From the configuration options (seen in &lt;code class=&quot;highlighter-rouge&quot;&gt;nignx -V&lt;/code&gt;) you can see from what
location Nginx is loading its modules in the option &lt;code class=&quot;highlighter-rouge&quot;&gt;--modules-path&lt;/code&gt;. If this
option is not specified, the modules are loaded from the &lt;code class=&quot;highlighter-rouge&quot;&gt;modules&lt;/code&gt; directory
under the path specified in &lt;code class=&quot;highlighter-rouge&quot;&gt;--prefix&lt;/code&gt;. In the example above the modules will be
loaded from &lt;code class=&quot;highlighter-rouge&quot;&gt;/opt/homebrew/Cellar/nginx/1.23.2/modules&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Copy the module to the modules directory. Then in the configuration add&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;load_module &quot;modules/ngx_hello_world_module.so&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Then start Nginx or reload config&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ nginx -s reload
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;To test the hello world module just add a test location to the config which is using
the directive defined in the module&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;location /test {
    hello_world;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Then reload the config and curl the new location:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ nginx -s reload
$ curl localhost/test
hello world
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2 id=&quot;build-a-static-module&quot;&gt;Build a static module&lt;/h2&gt;

&lt;p&gt;Building a static module means that the module is built into the Nginx binary,
so you basically build Nginx and specify a path to the module.&lt;/p&gt;

&lt;p&gt;Add this CMakeLists.txt into your module directory to build Nginx containing
the static module&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ExternalProject_Add(
    nginx
    DOWNLOAD_COMMAND URL https://nginx.org/download/nginx-1.18.0.tar.gz
    CONFIGURE_COMMAND cd ${CMAKE_CURRENT_BINARY_DIR}/nginx-prefix/src/nginx &amp;amp;&amp;amp; ./configure --add-module=${CMAKE_BINARY_DIR}/../ --with-compat --prefix=/opt/nginx
    BUILD_COMMAND make -C ${CMAKE_CURRENT_BINARY_DIR}/nginx-prefix/src/nginx
    INSTALL_COMMAND make -C ${CMAKE_CURRENT_BINARY_DIR}/nginx-prefix/src/nginx install
)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Build with:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cmake -B build
$ cmake --build build --parallel 8
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The resulting build can be found in &lt;code class=&quot;highlighter-rouge&quot;&gt;/opt/nginx&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To test the hello world module just add a test location using the directive
defined in the module. Note that you don’t need &lt;code class=&quot;highlighter-rouge&quot;&gt;load_module&lt;/code&gt; since the module
is already in the Nginx binary.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;location /test {
    hello_world;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Then reload the config and curl&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ nginx -s reload
$ curl localhost/test
hello world
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h1 id=&quot;references&quot;&gt;References&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/perusio/nginx-hello-world-module&quot;&gt;Nginx Hello World module&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.nginx.com/blog/compiling-dynamic-modules-nginx-plus/&quot;&gt;Compiling dynamic modules for nginx&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.nginx.com/resources/wiki/extending/converting/#compiling-dynamic&quot;&gt;Converting static module to dynamic&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.nginx.com/blog/nginx-dynamic-modules-how-they-work/&quot;&gt;How dynamic modules work&lt;/a&gt;&lt;/p&gt;

</description>
        <pubDate>Wed, 28 Dec 2022 00:00:00 +0100</pubDate>
        <link>http://hjortsberg.org/notes/Building-nginx-modules-using-cmake.html</link>
        <guid isPermaLink="true">http://hjortsberg.org/notes/Building-nginx-modules-using-cmake.html</guid>
        
        <category>nginx</category>
        
        
      </item>
    
      <item>
        <title>Set AWS Lambda trigger on an existing S3 bucket in CloudFormation template</title>
        <description>&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;An AWS Lambda can be triggered by events on an S3 bucket. A very useful feature
when e.g. a file upload is made and some operation should be done on the
uploaded file. The triggered Lambda can then read the file and perform these
operations.&lt;/p&gt;

&lt;p&gt;In AWS CloudFormation a Lambda trigger is set on the bucket. When the bucket is
defined and deployed in the CloudFormation template a &lt;code class=&quot;highlighter-rouge&quot;&gt;NotificationConfiguration&lt;/code&gt;
containing a &lt;code class=&quot;highlighter-rouge&quot;&gt;LambdaFunctionConfiguration&lt;/code&gt; is set on the bucket. However,
setting up such a trigger on an existing bucket (i.e. the bucket exist outside
of the stack) is a bit more complex and one method is described here.&lt;/p&gt;

&lt;h2 id=&quot;bucket-notification-configuration-using-custom-resource&quot;&gt;Bucket notification configuration using Custom Resource&lt;/h2&gt;

&lt;p&gt;A solution is to setup the notification configuration on the existing bucket
using an AWS CloudFormation Custom resource. An introduction to CloudFormation
custom resources can be &lt;a href=&quot;/notes/Introduction-to-AWS-CloudFormation-Custom-Resource.html&quot;&gt;found in a previous note&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;AWS has an &lt;a href=&quot;https://aws.amazon.com/premiumsupport/knowledge-center/cloudformation-s3-notification-lambda/&quot;&gt;article on the subject&lt;/a&gt;,
however, that description will not allow multiple notification configurations
on a bucket since it will overwrite the existing notification configuration (and
delete all notification configurations in the delete case).&lt;/p&gt;

&lt;p&gt;The solution presented here allows multiple stacks setting bucket notification
configuration on the same bucket and has some &lt;a href=&quot;#handle-non-existing-lambda-trigger-destination&quot;&gt;additional error handling&lt;/a&gt; to
prevent errors when creating or deleting the stack.&lt;/p&gt;

&lt;p&gt;The S3 bucket notification configuration is set as properties on the custom
resource. When the lambda resource handler is triggered by the custom resource
it reads the notification configuration and adds it to the bucket. When the stack
is deleted the lambda will also be triggered and should remove the notification
configuration from the bucket. An example is described below.&lt;/p&gt;

&lt;h1 id=&quot;cloudformation-template&quot;&gt;CloudFormation Template&lt;/h1&gt;

&lt;p&gt;In this example the existing bucket is passed in as the parameter &lt;code class=&quot;highlighter-rouge&quot;&gt;TestBucket&lt;/code&gt; to
the stack (the bucket name could also be imported using the intrinsic function
&lt;code class=&quot;highlighter-rouge&quot;&gt;Fn::ImportValue&lt;/code&gt;). The idea is that a &lt;code class=&quot;highlighter-rouge&quot;&gt;.png&lt;/code&gt; file added using the prefix
&lt;code class=&quot;highlighter-rouge&quot;&gt;images&lt;/code&gt; (e.g. &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;bucket&amp;gt;/images/faces/me.png&lt;/code&gt;) should trigger the Lambda
&lt;code class=&quot;highlighter-rouge&quot;&gt;TestLambda&lt;/code&gt; provided in the template.&lt;/p&gt;

&lt;h2 id=&quot;the-custom-resource&quot;&gt;The Custom Resource&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  BucketNotifications:
    Type: Custom::S3BucketNotifications
    Properties:
      ServiceToken:
        Fn::GetAtt:
          - BucketNotificationsHandler
          - Arn
      BucketName:
        Ref: TestBucket
      NotificationConfiguration:
        LambdaFunctionConfigurations:
          - Events:
              - s3:ObjectCreated:*
            Filter:
              Key:
                FilterRules:
                  - Name: suffix
                    Value: .png
                  - Name: prefix
                    Value: images
            LambdaFunctionArn:
              Fn::GetAtt:
                - TestLambda
                - Arn
      Managed: false
    DependsOn:
      - AllowTestBucketInvokeTestLambda
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;ServiceToken&lt;/code&gt; property is specifying the Lambda resource handler that will
be invoked when the Custom Resource is created. The rest of the properties are
to be read by that Lambda resource handler.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;NotificationConfiguration&lt;/code&gt; is the actual configuration to be set on the
bucket. Other than that there is the &lt;code class=&quot;highlighter-rouge&quot;&gt;Managed&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;BucketName&lt;/code&gt; properties, the
latter is obvious but &lt;code class=&quot;highlighter-rouge&quot;&gt;Managed&lt;/code&gt; might need a clarification. &lt;code class=&quot;highlighter-rouge&quot;&gt;Managed&lt;/code&gt; set to
&lt;code class=&quot;highlighter-rouge&quot;&gt;true&lt;/code&gt; means that the bucket is created in this template. That’s not the case
here, hence &lt;code class=&quot;highlighter-rouge&quot;&gt;Managed&lt;/code&gt; is set to &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When the bucket exist outside of the stack the bucket notification handler need
to be cautious that there could be other notifications on the bucket. Those
notifications should not be affected by the bucket notification handler. In the
other case, when the bucket is Managed (i.e. created in the stack) no such
considerations need to be made.&lt;/p&gt;

&lt;h2 id=&quot;custom-resource-handler&quot;&gt;Custom Resource handler&lt;/h2&gt;

&lt;p&gt;This is the actual bucket notification handler. A lambda with inline python code.
The code is actually what &lt;a href=&quot;https://docs.aws.amazon.com/cdk/v2/guide/home.html&quot;&gt;AWS CDK&lt;/a&gt;
is generating when setting bucket notification configuration using the CDK.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  BucketNotificationsHandler:
    Type: AWS::Lambda::Function
    Properties:
      Description: AWS CloudFormation handler for &quot;Custom::S3BucketNotifications&quot; resources (@aws-cdk/aws-s3)
      Handler: index.handler
      Role:
        Fn::GetAtt:
          - BucketNotificationsHandlerRole
          - Arn
      Runtime: python3.9
      Timeout: 300
      Code:
        ZipFile: |
          import boto3  # type: ignore
          import json
          import logging
          import urllib.request

          s3 = boto3.client(&quot;s3&quot;)
          l = boto3.client(&quot;lambda&quot;)

          CONFIGURATION_TYPES = [&quot;TopicConfigurations&quot;, &quot;QueueConfigurations&quot;, &quot;LambdaFunctionConfigurations&quot;]

          def handler(event: dict, context):
              response_status = &quot;SUCCESS&quot;
              error_message = &quot;&quot;
              try:
                  props = event[&quot;ResourceProperties&quot;]
                  bucket = props[&quot;BucketName&quot;]
                  notification_configuration = props[&quot;NotificationConfiguration&quot;]
                  request_type = event[&quot;RequestType&quot;]
                  managed = props.get('Managed', 'true').lower() == 'true'
                  stack_id = event['StackId']

                  if managed:
                    config = handle_managed(request_type, notification_configuration)
                  else:
                    config = handle_unmanaged(bucket, stack_id, request_type, notification_configuration)

                  put_bucket_notification_configuration(bucket, config)
              except Exception as e:
                  logging.exception(&quot;Failed to put bucket notification configuration&quot;)
                  response_status = &quot;FAILED&quot;
                  error_message = f&quot;Error: {str(e)}. &quot;
              finally:
                  submit_response(event, context, response_status, error_message)


          def handle_managed(request_type, notification_configuration):
            if request_type == 'Delete':
              return {}
            return notification_configuration


          def handle_unmanaged(bucket, stack_id, request_type, notification_configuration):

            # find external notifications
            external_notifications = find_external_notifications(bucket, stack_id)

            # if delete, that's all we need
            if request_type == 'Delete':
              return external_notifications

            def with_id(notification):
              notification['Id'] = f&quot;{stack_id}-{hash(json.dumps(notification, sort_keys=True))}&quot;
              return notification

            # otherwise, merge external with incoming config and augment with id
            notifications = {}
            for t in CONFIGURATION_TYPES:
              external = external_notifications.get(t, [])
              incoming = [with_id(n) for n in notification_configuration.get(t, [])]
              notifications[t] = external + incoming
            return notifications


          def find_external_notifications(bucket, stack_id):
            existing_notifications = get_bucket_notification_configuration(bucket)
            external_notifications = {}
            for t in CONFIGURATION_TYPES:
              # if the notification was created by us, we know what id to expect
              # so we can filter by it.
              external_notifications[t] = [n for n in existing_notifications
                .get(t, []) if not n['Id'].startswith(f&quot;{stack_id}-&quot;) and lambda_exist(n['LambdaFunctionArn'])]

            return external_notifications


          def lambda_exist(lambda_arn):
            try:
              l.get_function(FunctionName=lambda_arn)
            except Exception as e:
              if 'ResourceNotFound' in str(e):
                print(f'lambda {lambda_arn} does not exist. {e}')
                return False
            return True


          def get_bucket_notification_configuration(bucket):
            return s3.get_bucket_notification_configuration(Bucket=bucket)


          def put_bucket_notification_configuration(bucket, notification_configuration):
            s3.put_bucket_notification_configuration(Bucket=bucket, NotificationConfiguration=notification_configuration)


          def submit_response(event: dict, context, response_status: str, error_message: str):
              response_body = json.dumps(
                  {
                      &quot;Status&quot;: response_status,
                      &quot;Reason&quot;: f&quot;{error_message}See the details in CloudWatch Log Stream: {context.log_stream_name}&quot;,
                      &quot;PhysicalResourceId&quot;: event.get(&quot;PhysicalResourceId&quot;) or event[&quot;LogicalResourceId&quot;],
                      &quot;StackId&quot;: event[&quot;StackId&quot;],
                      &quot;RequestId&quot;: event[&quot;RequestId&quot;],
                      &quot;LogicalResourceId&quot;: event[&quot;LogicalResourceId&quot;],
                      &quot;NoEcho&quot;: False,
                  }
              ).encode(&quot;utf-8&quot;)
              headers = {&quot;content-type&quot;: &quot;&quot;, &quot;content-length&quot;: str(len(response_body))}
              try:
                  req = urllib.request.Request(url=event[&quot;ResponseURL&quot;], headers=headers, data=response_body, method=&quot;PUT&quot;)
                  with urllib.request.urlopen(req) as response:
                      print(response.read().decode(&quot;utf-8&quot;))
                  print(&quot;Status code: &quot; + response.reason)
              except Exception as e:
                  print(&quot;send(..) failed executing request.urlopen(..): &quot; + str(e))
    DependsOn:
      - BucketNotificationsHandlerRoleDefaultPolicy
      - BucketNotificationsHandlerRole
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h3 id=&quot;basic-idea&quot;&gt;Basic idea&lt;/h3&gt;

&lt;p&gt;The Lambda will find all notifications on the bucket and will then add or remove
the notification configuration for the current stack to/from the list of
notification configurations and then re-set the configuration on the bucket.&lt;/p&gt;

&lt;h3 id=&quot;handle-non-existing-lambda-trigger-destination&quot;&gt;Handle non-existing lambda trigger destination&lt;/h3&gt;

&lt;p&gt;As mentioned the custom resource lambda handler code is what CDK is generating -
with one addition. There is a check added to make sure the lambda that should be
triggered exists (the call to &lt;code class=&quot;highlighter-rouge&quot;&gt;lambda_exist()&lt;/code&gt;. Otherwise, if the lambda does not
exist any operation (create, update or delete) on the stack would fail.
This is a corner case but can happen when having multiple CloudFormation stacks
where the stacks contain a lambda that is triggered by an external S3 bucket
and the stacks are deleted in parallel.&lt;/p&gt;

&lt;p&gt;The call Traceback for the error would look like;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Failed to put bucket notification configuration
Traceback (most recent call last):
File &quot;/var/task/index.py&quot;, line 27, in handler
put_bucket_notification_configuration(bucket, config)
File &quot;/var/task/index.py&quot;, line 87, in put_bucket_notification_configuration
s3.put_bucket_notification_configuration(Bucket=bucket, NotificationConfiguration=notification_configuration)
File &quot;/var/runtime/botocore/client.py&quot;, line 391, in _api_call
return self._make_api_call(operation_name, kwargs)
File &quot;/var/runtime/botocore/client.py&quot;, line 719, in _make_api_call
raise error_class(parsed_response, operation_name)
botocore.exceptions.ClientError: An error occurred (InvalidArgument) when calling the PutBucketNotificationConfiguration operation: Unable to validate the following destination configurations
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;I.e. the destination notification configuration is incorrect since one of the
lambdas does not exist.&lt;/p&gt;

&lt;h2 id=&quot;permissions&quot;&gt;Permissions&lt;/h2&gt;

&lt;p&gt;The S3 bucket need to have &lt;code class=&quot;highlighter-rouge&quot;&gt;InvokeFunction&lt;/code&gt; permission on the target Lambda&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  AllowTestBucketInvokeTestLambda:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName:
        Fn::GetAtt:
          - TestLambda
          - Arn
      Principal: s3.amazonaws.com
      SourceAccount:
        Ref: AWS::AccountId
      SourceArn:
        !Join
          - ''
          - - 'arn:aws:s3:::'
            - !Ref TestBucket
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The custom resource handler lambda should assume Lambda Basic Execution Role
(for logging to CloudWatch Logs):&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  BucketNotificationsHandlerRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
        Version: &quot;2012-10-17&quot;
      ManagedPolicyArns:
        - Fn::Join:
            - &quot;&quot;
            - - &quot;arn:&quot;
              - Ref: AWS::Partition
              - :iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The custom resource handler lambda need permission to add bucket notification on
the bucket&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  BucketNotificationsHandlerRoleDefaultPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyDocument:
        Statement:
          - Action: s3:*BucketNotification
            Effect: Allow
            Resource: &quot;*&quot;
        Version: &quot;2012-10-17&quot;
      PolicyName: BucketNotificationsHandlerRoleDefaultPolicy
      Roles:
        - Ref: BucketNotificationsHandlerRole
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h1 id=&quot;summary&quot;&gt;Summary&lt;/h1&gt;

&lt;p&gt;A working example can be found &lt;a href=&quot;https://github.com/bhjortsberg/aws-cloudformation-templates/blob/master/bucketnotification.yaml&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Deploy the template using AWS Console or AWS Cli using:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ aws cloudformation create-stack --stack-name bucket-notification-demo --template-body file://bucketnotification.yaml --capabilities CAPABILITY_IAM --parameters ParameterKey=TestBucket,ParameterValue=&amp;lt;your-bucket&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h1 id=&quot;resources&quot;&gt;Resources&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources.html&quot;&gt;AWS Custom Resource&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://aws.amazon.com/premiumsupport/knowledge-center/cloudformation-s3-notification-lambda/&quot;&gt;AWS Notification config on existing bucket with cloudformation&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Fri, 05 Aug 2022 00:00:00 +0200</pubDate>
        <link>http://hjortsberg.org/notes/Set-AWS-Lambda-trigger-on-an-existing-S3-bucket-in-CloudFormation-template.html</link>
        <guid isPermaLink="true">http://hjortsberg.org/notes/Set-AWS-Lambda-trigger-on-an-existing-S3-bucket-in-CloudFormation-template.html</guid>
        
        <category>AWS,</category>
        
        <category>Cloud</category>
        
        
      </item>
    
      <item>
        <title>Introduction to AWS CloudFormation Custom Resource</title>
        <description>&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;This note describes the &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources.html&quot;&gt;AWS CloudFormation Custom resource&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A custom resource that fetches the API Key from a deployed AWS API Gateway is
shown as an example to demonstrate how custom resources work and can be used.&lt;/p&gt;

&lt;h1 id=&quot;cloudformation-custom-resource&quot;&gt;CloudFormation Custom Resource&lt;/h1&gt;

&lt;p&gt;A Custom Resource has only one required property, the &lt;code class=&quot;highlighter-rouge&quot;&gt;ServiceToken&lt;/code&gt;. The
&lt;code class=&quot;highlighter-rouge&quot;&gt;ServiceToken&lt;/code&gt; can either be the ARN of a Lamda or an SNS. In the case of a
lambda the lambda will be called on create, delete or update of the custom
resource. When the lambda is called all properties of the custom resource can be
found in the &lt;code class=&quot;highlighter-rouge&quot;&gt;ResourceProperty&lt;/code&gt; of the request object.&lt;/p&gt;

&lt;p&gt;A custom resource need to signal that deployment succeeded (or failed) and does
so by sending a response object to the response URL that can be found in the
&lt;code class=&quot;highlighter-rouge&quot;&gt;ResponseURL&lt;/code&gt; of the request object.&lt;/p&gt;

&lt;p&gt;This is basically how a Custom Resource is defined in the template. In this
example, a CloudFormation parameter is passed in to the Custom Resource using
&lt;code class=&quot;highlighter-rouge&quot;&gt;Fn::Ref&lt;/code&gt; Intrinsic function and the parameter should contain the API Key Id.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Parameters:
  ApiKeyIdParam:
    Type: String

Resources:
  GetApiKey:
    Type: Custom::GetApiKey
    Properties:
      ServiceToken: !GetAtt GetApiKeyResourceHandler.Arn
      ApiKeyId: !Ref ApiKeyIdParam
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The Arn for the Lambda to be invoked is assigned to the &lt;code class=&quot;highlighter-rouge&quot;&gt;ServiceToken&lt;/code&gt; property,
in this example the Lambda resource is called &lt;code class=&quot;highlighter-rouge&quot;&gt;GetApiKeyResourceHandler&lt;/code&gt;. The
parameter &lt;code class=&quot;highlighter-rouge&quot;&gt;ApiKeyId&lt;/code&gt; passed in as a property will be read by the handler lambda
&lt;code class=&quot;highlighter-rouge&quot;&gt;GetApiKeyResourceHandler&lt;/code&gt;.&lt;/p&gt;

&lt;h1 id=&quot;resource-handler-lambda&quot;&gt;Resource handler Lambda&lt;/h1&gt;

&lt;p&gt;Any input needed by the logic implemented in the Custom Resource should be set
as properties on the custom resource. The properties will then be available in
the &lt;code class=&quot;highlighter-rouge&quot;&gt;ResourceProperty&lt;/code&gt; of the request object and can be read by the lambda
handler. The lambda handler can then e.g. use AWS SDK to read or manipulate
other resources.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  GetApiKeyResourceHandler:
    Type: AWS::Lambda::Function
    Properties:
      Description: Handler for GetApiKey Custom Resource
      Handler: index.handler
      Runtime: python3.9
      Role: !GetAtt GetApiKeyResourceHandlerRole.Arn
      Code:
        ZipFile: |
          import boto3
          import json
          import urllib.request

          def handler(event, context):
              response_status = &quot;SUCCESS&quot;

              if event['RequestType'] != 'Create':
                # Only care about stack Create opertations
                send_response(event, response_status)
                return

              api_key_id = event['ResourceProperties'].get('ApiKeyId')
              client = boto3.client('apigateway')
              message = ''
              data = {}

              if api_key_id:
                  try:
                      r = client.get_api_key(apiKey = api_key_id, includeValue=True)
                      # Add output
                      data = { &quot;ApiKey&quot;: r['value'] }
                  except Exception as e:
                      response_status = &quot;FAILED&quot;
                      print(f'Error: {e}')
                      message = f'Failed to deploy {str(e)}'

              send_response(event, response_status, message, data)

          def send_response(event, status, message='', data={}):
              # Fill the required response properties
              response = {
                  &quot;Status&quot;: status,
                  &quot;Reason&quot;: message,
                  &quot;PhysicalResourceId&quot;: event.get('PhysicalResourceId') or event['LogicalResourceId'],
                  &quot;StackId&quot;: event['StackId'],
                  &quot;RequestId&quot;: event['RequestId'],
                  &quot;LogicalResourceId&quot;: event['LogicalResourceId'],
                  &quot;Data&quot;: data}
              # Send the response
              response_body = json.dumps(response).encode('utf-8')
              req = urllib.request.Request(url=event['ResponseURL'], data=response_body, method=&quot;PUT&quot;)
              with urllib.request.urlopen(req) as r:
                  print(r.read().decode('utf-8'))
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;GetApiKeyResourceHandler&lt;/code&gt; is a Lambda with inline python code. As defined
in the template the handler is the function named &lt;code class=&quot;highlighter-rouge&quot;&gt;handler()&lt;/code&gt; and starts by reading
the property that was passed in. Then the Lambda is getting the API key from the
API Gateway using the AWS Python SDK (called Boto3),&lt;/p&gt;

&lt;p&gt;Finally a response is sent to the &lt;code class=&quot;highlighter-rouge&quot;&gt;ResponseURL&lt;/code&gt; that can be found in the request
object passed to the lambda. The response object should have &lt;code class=&quot;highlighter-rouge&quot;&gt;Status: SUCCESS&lt;/code&gt;
to signal that the resource was successfull deployed.&lt;/p&gt;

&lt;h2 id=&quot;request-type&quot;&gt;Request Type&lt;/h2&gt;

&lt;p&gt;The request object also contains a &lt;code class=&quot;highlighter-rouge&quot;&gt;RequestType&lt;/code&gt; attribute which makes it
possible to check if the custom resource is created, updated or deleted.
Available &lt;code class=&quot;highlighter-rouge&quot;&gt;RequestTypes&lt;/code&gt; are &lt;code class=&quot;highlighter-rouge&quot;&gt;Create, Update, Delete&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;error-handling&quot;&gt;Error handling&lt;/h2&gt;

&lt;p&gt;If there are errors in the logic, the &lt;code class=&quot;highlighter-rouge&quot;&gt;Status&lt;/code&gt; attribute in the response object
should be set to &lt;code class=&quot;highlighter-rouge&quot;&gt;Status: FAILED&lt;/code&gt;. Setting the status to FAILED will cause the
deploy to fail and the CloudFormation stack will rollback. You can also set a
helpful error message in the &lt;code class=&quot;highlighter-rouge&quot;&gt;Reason&lt;/code&gt; property: &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;Reason&quot;: &quot;Failed to ....&quot;&lt;/code&gt;.
The error message will be shown in the CloudFormation event log.&lt;/p&gt;

&lt;h2 id=&quot;return-value-from-a-custom-resource&quot;&gt;Return value from a Custom Resource&lt;/h2&gt;

&lt;p&gt;The Custom Resource Handler may produce a value or some other result that need
to be propagated further. This is done by setting a &lt;code class=&quot;highlighter-rouge&quot;&gt;Data&lt;/code&gt; object in the
response object. In the example above the &lt;code class=&quot;highlighter-rouge&quot;&gt;Data&lt;/code&gt; object is set to:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;data = { &quot;ApiKey&quot;: r['value'] }
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;And then that value can be passed as an output (or passed to other resources) on
the CloudFormation stack using the &lt;code class=&quot;highlighter-rouge&quot;&gt;GetAtt&lt;/code&gt; intrinsic function.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Outputs:
  ApiKey:
    Description: 'API Key as Output from Custom Resource'
    Value: !GetAtt GetApiKey.ApiKey
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2 id=&quot;permissions&quot;&gt;Permissions&lt;/h2&gt;

&lt;p&gt;Define the role referenced by the Custom Resource Handler. This role need to
have policies that allows the lambda to perform its logic. In this example the
logic need permission to perform &lt;code class=&quot;highlighter-rouge&quot;&gt;apigateway:GET&lt;/code&gt; action.  Also the
&lt;code class=&quot;highlighter-rouge&quot;&gt;AWSLambdaBasicExecutionRole&lt;/code&gt; is set (which is only a policy allowing to write
CloudWatch logs).&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  GetApiKeyResourceHandlerRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
        Version: &quot;2012-10-17&quot;
      ManagedPolicyArns:
        - Fn::Join:
          - &quot;&quot;
          - - &quot;arn:&quot;
            - Ref: AWS::Partition
            - :iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyName: AllowApiGatewayGet
          PolicyDocument:
            Statement:
            - Action: apigateway:GET
              Effect: Allow
              Resource: &quot;*&quot;
            Version: &quot;2012-10-17&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h1 id=&quot;problems-and-caveats&quot;&gt;Problems and Caveats&lt;/h1&gt;

&lt;p&gt;It is possible to screw up a bit. Any errors in the Custom Resource handler
code which makes the lambda crash before the response is sent will make
CloudFormation wait until there is a timeout. The timeout is very long, like
30 minutes or so.&lt;/p&gt;

&lt;p&gt;Note that this also happens on errors that you normally don’t handle in the code
with exceptions e.g. syntax errors or importing unavailable modules.&lt;/p&gt;

&lt;p&gt;The stack will stay in &lt;code class=&quot;highlighter-rouge&quot;&gt;CREATE_IN_PROGRESS&lt;/code&gt; or if the error happens when the
stack is deleted it will stay in &lt;code class=&quot;highlighter-rouge&quot;&gt;DELETE_IN_PROGRESS&lt;/code&gt; until CloudFormation times
out.&lt;/p&gt;

&lt;p&gt;The problem causing the crash can probably be seen in the CloudWatch logs for
the Custom Resource handler. In this example they are found in
`CloudWatch/Log Groups/aws/lambda/&lt;StackName&gt;-GetApiKeyResourceHandler&lt;/StackName&gt;&lt;/p&gt;

&lt;h1 id=&quot;summary&quot;&gt;Summary&lt;/h1&gt;

&lt;p&gt;The CloudFormation template used in this example can be found &lt;a href=&quot;https://github.com/bhjortsberg/aws-cloudformation-templates/blob/master/get-apikey-resource-template.yaml&quot;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Deploy the template using AWS Console or using AWS CLI:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ aws cloudformation create-stack --stack-name custom-resource-demo --template-body file://template.yaml --capabilities CAPABILITY_IAM --parameters ParameterKey=ApiKeyIdParam,ParameterValue=xxxyyyzzza
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h1 id=&quot;resources&quot;&gt;Resources&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/crpg-ref-requests.html&quot;&gt;Custom Resource request&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/crpg-ref-responses.html&quot;&gt;Custom Resource response&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 31 Mar 2022 00:00:00 +0200</pubDate>
        <link>http://hjortsberg.org/notes/Introduction-to-AWS-CloudFormation-Custom-Resource.html</link>
        <guid isPermaLink="true">http://hjortsberg.org/notes/Introduction-to-AWS-CloudFormation-Custom-Resource.html</guid>
        
        <category>AWS,</category>
        
        <category>Cloud</category>
        
        
      </item>
    
      <item>
        <title>Hide the Android navigation bar using WindowInsetsController</title>
        <description>&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;Documentation describing how to hide the navigation bar and/or running an
Android application in fullscreen usually uses the &lt;code class=&quot;highlighter-rouge&quot;&gt;View.SYSTEM_UI_FLAG_HIDE_NAVIGATION&lt;/code&gt;
and &lt;code class=&quot;highlighter-rouge&quot;&gt;View.SYSTEM_UI_FLAG_FULLSCREEN&lt;/code&gt; flags, e.g. &lt;a href=&quot;https://developer.android.com/training/system-ui/navigation&quot;&gt;Android developer documents&lt;/a&gt;
and &lt;a href=&quot;https://developer.android.com/training/system-ui/immersive&quot;&gt;a another Android developer doc&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;However &lt;code class=&quot;highlighter-rouge&quot;&gt;SYSTEM_UI_FLAG_FULLSCREEN&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;SYSTEM_UI_FLAG_HIDE_NAVIGATION&lt;/code&gt; are
marked deprecated together with the other &lt;code class=&quot;highlighter-rouge&quot;&gt;SYSTEM_UI&lt;/code&gt;-flags. This note describes
how to hide navigation bar in the new way using the &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowInsetsController&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;example&quot;&gt;Example&lt;/h2&gt;

&lt;p&gt;There is a working demo/test written in Kotlin of &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowInsetsController&lt;/code&gt; on
my &lt;a href=&quot;https://github.com/bhjortsberg/WindowInsetsControllerTest&quot;&gt;github&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;fullscreen-mode&quot;&gt;Fullscreen mode&lt;/h1&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;WindowInsetController&lt;/code&gt; class have methods that is supposed to replace
the use of the deprecated &lt;code class=&quot;highlighter-rouge&quot;&gt;SYSTEM_UI_FLAG_FULLSCREEN&lt;/code&gt;, however the methods used
for hiding the bars are only available in Android 11 and newer (i.e. API
Level &amp;gt; 30). This is solved by using the wrapper class
&lt;code class=&quot;highlighter-rouge&quot;&gt;WindowInsetsControllerCompat&lt;/code&gt; who will handle checking the API-level of the
device running the application and make it work for all API-levels.&lt;/p&gt;

&lt;p&gt;For fullscreen, hide the navigation and status bars.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;WindowInsetsControllerCompat controller = new WindowInsetsControllerCompat(getWindow(), getWindow().getDecorView());
controller.hide(WindowInsetsCompat.Type.systemBars());
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;To allow the bars to be shown when swiping from the edge of the screen one can
set system bars behaviour like this:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;controller.setSystemBarsBehavior(WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h1 id=&quot;making-your-own-transient-navigation-bar&quot;&gt;Making your own transient navigation bar&lt;/h1&gt;

&lt;p&gt;Under some circumstances the navigation bar did not show up when swiping from
the edge of the screen on my application. Unsure why it happened but it was
often the case after the screen was rotated.&lt;/p&gt;

&lt;p&gt;I made a workaround to show the navigation bar when tapping on the screen
instead.&lt;/p&gt;

&lt;p&gt;Show the navigation bar for 3s when tapping on the layout:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mainlayout.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        View decorView = getWindow().getDecorView();
        WindowInsetsControllerCompat controller = new WindowInsetsControllerCompat(getWindow(), decorView);
        controller.show(WindowInsetsCompat.Type.navigationBars());
        decorView.postDelayed(new Runnable() {
            @Override
            public void run() {
                controller.hide(WindowInsetsCompat.Type.systemBars());
            }
        }, 3000);
    }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Note that the above is an example and it may not be possible to
use the main layout for capturing the click events.&lt;/p&gt;

&lt;h1 id=&quot;translucent-navigation-bar&quot;&gt;Translucent Navigation bar&lt;/h1&gt;

&lt;p&gt;When the Navigation bar is auto-hiding it looks nice if it also is made
translucent. Add the following to the &lt;code class=&quot;highlighter-rouge&quot;&gt;style&lt;/code&gt; element in &lt;code class=&quot;highlighter-rouge&quot;&gt;themes.xml&lt;/code&gt; (or the
resource file containing the &lt;code class=&quot;highlighter-rouge&quot;&gt;style&lt;/code&gt; e.g. &lt;code class=&quot;highlighter-rouge&quot;&gt;style.xml&lt;/code&gt;)&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;item name=&quot;android:windowTranslucentNavigation&quot;&amp;gt;true&amp;lt;/item&amp;gt;
    &amp;lt;item name=&quot;android:fitsSystemWindows&quot;&amp;gt;true&amp;lt;/item&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The second line is needed in order to make the activity content display correct.
Without it some parts of the content may be laid out outside of the screen like
this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/android-navigation/netpal_noFitsSystemWin.png&quot; width=&quot;400&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Compared to when using a translucent navigation bar and &lt;code class=&quot;highlighter-rouge&quot;&gt;fitsSystemWindows: true&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/android-navigation/netpal_withFitsSystemWin.png&quot; width=&quot;400&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;resources&quot;&gt;Resources&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://proandroiddev.com/exploring-windowinsets-on-android-11-a80cf8fe19be&quot;&gt;Exploring WindowInsets on Android 11&lt;/a&gt;&lt;/p&gt;

</description>
        <pubDate>Wed, 15 Sep 2021 00:00:00 +0200</pubDate>
        <link>http://hjortsberg.org/notes/Hide-the-Android-navigation-bar-using-WindowInsetsController.html</link>
        <guid isPermaLink="true">http://hjortsberg.org/notes/Hide-the-Android-navigation-bar-using-WindowInsetsController.html</guid>
        
        <category>Android</category>
        
        
      </item>
    
      <item>
        <title>Setup TCP and UDP forwarding to Android Emulator</title>
        <description>&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;This is a note describing the steps needed to forward TCP and UDP traffic to
the Android Emulator.&lt;/p&gt;

&lt;p&gt;Sometimes when developing an application one need to connect and send data from
the host machine to the app running in an Android Emulator. In order to do that
there are some tricks needed for forwarding the traffic.&lt;/p&gt;

&lt;h1 id=&quot;forward-tcp-traffic-to-android-emulator&quot;&gt;Forward TCP traffic to Android Emulator&lt;/h1&gt;

&lt;p&gt;Forwarding TCP traffic to android emulator is simple. Just use the &lt;code class=&quot;highlighter-rouge&quot;&gt;adb&lt;/code&gt; tool
like this to forward host port 2222 to port 3333 on the Android emulator:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ adb forward tcp:2222 tcp:3333
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Check with&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ adb forward --list
emulator-5554 tcp:2222 tcp:3333
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h1 id=&quot;forward-udp-traffic-to-android-emulator&quot;&gt;Forward UDP traffic to Android Emulator&lt;/h1&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;forward&lt;/code&gt; command in &lt;code class=&quot;highlighter-rouge&quot;&gt;adb&lt;/code&gt; tool can only forward TCP traffic. In order to
forward UDP traffic one need to telnet into the emulator itself and run a
command.&lt;/p&gt;

&lt;p&gt;First you need the emulators auth token. The auth token can be found by doing&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cat ~/.emulator_console_auth_token
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;When the emulator is running it opens a control port on port 5554, Connect to
the port using &lt;code class=&quot;highlighter-rouge&quot;&gt;telnet&lt;/code&gt; and use the command&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;auth &amp;lt;token&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;to authenticate. Then use the &lt;code class=&quot;highlighter-rouge&quot;&gt;redir&lt;/code&gt; command to forward host UDP port 2222 to
emulator port 3333. This is the complete procedure:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ telnet localhost 5554
Trying ::1...
Connected to localhost.
Escape character is '^]'.
Android Console: Authentication required
Android Console: type 'auth &amp;lt;auth_token&amp;gt;' to authenticate
Android Console: you can find your &amp;lt;auth_token&amp;gt; in
'/Users/bjorn/.emulator_console_auth_token'
OK
auth sU6**********XS8
Android Console: type 'help' for a list of commands
OK
redir
allows you to add, list and remove UDP and/or PORT redirection from the host to the device
as an example, 'redir  tcp:5000:6000' will route any packet sent to the host's TCP port 5000
to TCP port 6000 of the emulated device

available sub-commands:
    list             list current redirections
    add              add new redirection
    del              remove existing redirection

redir add udp:2222:3333
OK
redir list
udp:2222  =&amp;gt; 3333
OK
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;So as seen in the &lt;code class=&quot;highlighter-rouge&quot;&gt;redir list&lt;/code&gt; command host UDP port 2222 is now forwarded to
port 3333 on the emulator.&lt;/p&gt;

&lt;h1 id=&quot;resource&quot;&gt;Resource&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://developer.android.com/studio/run/emulator-networking&quot;&gt;Android Developer - emulator networking&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 25 Aug 2021 00:00:00 +0200</pubDate>
        <link>http://hjortsberg.org/notes/Setup-TCP-and-UDP-forwarding-to-Android-Emulator.html</link>
        <guid isPermaLink="true">http://hjortsberg.org/notes/Setup-TCP-and-UDP-forwarding-to-Android-Emulator.html</guid>
        
        <category>Android</category>
        
        
      </item>
    
      <item>
        <title>Setting up software RAID in linux</title>
        <description>&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;Setting up software RAID in Linux is very easy, it is also possible to
simulate a disk failure and verify that the array is working. The installation
described here was made on ArchLinux but apart from installation command the
instructions should be the same for any Linux distribution.&lt;/p&gt;

&lt;h1 id=&quot;installing-software-raid-manangement-tool&quot;&gt;Installing Software RAID manangement tool&lt;/h1&gt;

&lt;p&gt;Install the linux md tool in ArchLinux:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo pacman -S mdadm 
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h1 id=&quot;configuring-raid-array&quot;&gt;Configuring RAID array&lt;/h1&gt;

&lt;p&gt;I have four disks &lt;code class=&quot;highlighter-rouge&quot;&gt;sdb&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;sdc&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;sdd&lt;/code&gt;, and &lt;code class=&quot;highlighter-rouge&quot;&gt;sde&lt;/code&gt; which I  want to add to a RAID5 array.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cat /proc/partitions 
major minor  #blocks  name

   8        0    8388608 sda
   8        1    8387584 sda1
   8       16     524288 sdb
   8       32     524288 sdc
   8       48     524288 sdd
   8       64     524288 sde
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;In this example my disks are four 512MB disks (this example is actually from a
virtual machine). RAID5 means that size for the array will be the sum of all
minus 1 of the disks. I.e. in this case with four 0.5GB disks the size of the
array will be 1.5GB. The non available space is used to store checksums for data
which makes it possible to recreate data from a failed disk.
In RAID5 it is possible to recover from one failed disk. RAID6 can recover from
two failed disks, but RAID6 will also use 2 disks that are unavailable for
storage on the array.&lt;/p&gt;

&lt;p&gt;Creating a RAID5 array&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo mdadm --create /dev/md0 --chunk=32K --level=raid5 --raid-devices=4  /dev/sdb /dev/sdc /dev/sdd /dev/sde
mdadm: Defaulting to version 1.2 metadata
mdadm: array /dev/md0 started.
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h1 id=&quot;use-the-device&quot;&gt;Use the device&lt;/h1&gt;

&lt;p&gt;Create filesystem and mount&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo mkfs.ext4 /dev/md0
$ sudo mount /dev/md0 /media/raid
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h1 id=&quot;checking-the-raid-array-progress&quot;&gt;Checking the raid array progress&lt;/h1&gt;

&lt;p&gt;Check the status of the RAID array:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cat /proc/mdstat 
Personalities : [raid6] [raid5] [raid4] 
md0 : active raid5 sde[4] sdd[2] sdc[1] sdb[0]
      1569792 blocks super 1.2 level 5, 32k chunk, algorithm 2 [4/4] [UUUU]
      
unused devices: &amp;lt;none&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;[UUUU]&lt;/code&gt; shows the status of each disk in the array, all disks are &lt;code class=&quot;highlighter-rouge&quot;&gt;UP&lt;/code&gt;.
A degraded array would show &lt;code class=&quot;highlighter-rouge&quot;&gt;_&lt;/code&gt; for the faulty drive.&lt;/p&gt;

&lt;p&gt;To get more details on the raid array:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo mdadm -D /dev/md0 
/dev/md0:
           Version : 1.2
     Creation Time : Tue May 19 22:19:54 2020
        Raid Level : raid5
        Array Size : 1569792 (1533.00 MiB 1607.47 MB)
     Used Dev Size : 523264 (511.00 MiB 535.82 MB)
      Raid Devices : 4
     Total Devices : 4
       Persistence : Superblock is persistent

       Update Time : Tue May 19 22:20:01 2020
             State : clean 
    Active Devices : 4
   Working Devices : 4
    Failed Devices : 0
     Spare Devices : 0

            Layout : left-symmetric
        Chunk Size : 32K

Consistency Policy : resync

              Name : arch:0  (local to host arch)
              UUID : 35300d92:56f766ae:d945154a:daa7456e
            Events : 18

    Number   Major   Minor   RaidDevice State
       0       8       16        0      active sync   /dev/sdb
       1       8       32        1      active sync   /dev/sdc
       2       8       48        2      active sync   /dev/sdd
       4       8       64        3      active sync   /dev/sde

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h1 id=&quot;testing-the-raid-array&quot;&gt;Testing the RAID array&lt;/h1&gt;

&lt;p&gt;Simulate a faulty drive:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo mdadm --manage --set-faulty /dev/md0 /dev/sdb
mdadm: set /dev/sdb faulty in /dev/md0
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The drive &lt;code class=&quot;highlighter-rouge&quot;&gt;/dev/sdb&lt;/code&gt; is now faulty but the array can still be used.&lt;/p&gt;

&lt;p&gt;To recover the faulty drive remove the drive from the raid&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo mdadm /dev/md0 -r /dev/sdb
mdadm: hot removed /dev/sdb from /dev/md0
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Add drive to the raid array again&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo mdadm /dev/md0 -a /dev/sdb
mdadm: added /dev/sdb
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The array will now start to rebuild. The rebuild process can be monitored on
&lt;code class=&quot;highlighter-rouge&quot;&gt;/proc/mdstat&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cat /proc/mdstat
Personalities : [raid6] [raid5] [raid4]
md0 : active raid5 sdb[6] sde[4] sdd[5] sdc[1]
      1569792 blocks super 1.2 level 5, 32k chunk, algorithm 2 [4/3] [_UUU]
      [========&amp;gt;............]  recovery = 44.5% (233692/523264) finish=0.0min speed=233692K/sec
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;After the progress bar there is an estimate on how much time is left for the
rebuild process, in this case the disks are so small so the rebuild process is
really fast. If the disks are large and the system is in use with some disk
IO load and CPU load the process can take quite a lot of time.&lt;/p&gt;

&lt;p&gt;To see what is happening one can  monitor disk io and cpu load during rebuild&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
$ iostat -k 5

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           0,21    0,00   49,06    2,49    0,00   48,23

Device             tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd
md0               0,00         0,00         0,00         0,00          0          0          0
sda               0,40         0,00         3,20         0,00          0         16          0
sdb             142,80        30,70     84257,50         0,00        153     421287          0
sdc             137,20     84793,60         1,40         0,00     423968          7          0
sdd             137,00     84792,80         1,40         0,00     423964          7          0
sde             137,00     84792,80         1,40         0,00     423964          7          0
scd0              0,00         0,00         0,00         0,00          0          0          0

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;As can be seen on the disk IO, data is read from sdc/sdd/sde and written to
sdb. This system was completly unused during the rebuild process.&lt;/p&gt;

&lt;h1 id=&quot;references&quot;&gt;References&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://raid.wiki.kernel.org/index.php/RAID_setup&quot;&gt;RAID setup&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://raid.wiki.kernel.org/index.php/Mdstat&quot;&gt;Explaining mdstat info&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cyberciti.biz/tips/linux-raid-increase-resync-rebuild-speed.html&quot;&gt;Speeding up RAID rebuild time&lt;/a&gt;&lt;/p&gt;

</description>
        <pubDate>Tue, 21 Jul 2020 00:00:00 +0200</pubDate>
        <link>http://hjortsberg.org/notes/Setting-up-software-RAID-in-linux.html</link>
        <guid isPermaLink="true">http://hjortsberg.org/notes/Setting-up-software-RAID-in-linux.html</guid>
        
        <category>Linux</category>
        
        
      </item>
    
      <item>
        <title>Support Google AdMob Ads in Godot Android games</title>
        <description>&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;In a previous note I wrote about &lt;a href=&quot;/notes/Create-Android-Game-Applications-from-Godot.html&quot;&gt;creating Android games from an existing Godot
project&lt;/a&gt;
. This is a continuation on that note and will explore the possibility to add
Google AdMob ads in an Android game created with Godot. Adding AdMob ads support
to Godot requires rebuilding the export templates, hence, that step is described
in detail here.&lt;/p&gt;

&lt;h2 id=&quot;google-admob&quot;&gt;Google AdMob&lt;/h2&gt;

&lt;p&gt;When using AdMob for real you need to create an application and an Ad Unit in
Google AdMob. It is also possible to use test ads when developing the game.&lt;/p&gt;

&lt;p&gt;Create a &lt;a href=&quot;https://admob.google.com&quot;&gt;Google AdMob&lt;/a&gt; account. Then sign in and
create an application by clicking Apps –&amp;gt; Add App. You will be asked if you
have uploaded the App to Play Store. Next you need to setup the new app, write
a name and select platform:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/godot_admob/1-admob-setup-new-app.png&quot; alt=&quot;alt attribute&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now your app will get an AdMob AppId which is needed later when exporting the
game from Godot. Click on Create Ad Unit&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/godot_admob/2-admob-setup-new-app-done.png&quot; alt=&quot;alt attribute&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Select an Ad format, in this example a Banner used.&lt;/p&gt;

&lt;p&gt;In the next step add a name for the ad unit.
You can make some additional configuration in the Advanced Setting, e.g. choose
if the ad should play a movie etc. When finished click Create Ad Unit&lt;/p&gt;

&lt;p&gt;Now you have an Ad Unit Id and an App Id. Both will be needed later.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/godot_admob/5-admob-create-adunit-done.png&quot; alt=&quot;alt attribute&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You can later find your App and Ad Unit in AdMob user interface where you also
will be able to find the App Id and Unit Id.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/godot_admob/6-admob-list-adunits.png&quot; alt=&quot;alt attribute&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;build-export-templates-with-support-for-third-party-admob-module&quot;&gt;Build export templates with support for third party AdMob module&lt;/h1&gt;

&lt;p&gt;There are a few cases when rebuilding export templates are required. In
the Android case for example any changes that normally are done in a projects
&lt;code class=&quot;highlighter-rouge&quot;&gt;build.gradle&lt;/code&gt; like &lt;code class=&quot;highlighter-rouge&quot;&gt;targetSdkVersion&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;minSdkVersion&lt;/code&gt; require you to
rebuild the export templates.&lt;/p&gt;

&lt;p&gt;Adding a third party module in Godot may also require you to rebuild the export
templates. In this case the AdMob module for supporting ads in the exported
Android game is being added.&lt;/p&gt;

&lt;h2 id=&quot;getting-godot-and-the-admob-module&quot;&gt;Getting Godot and the AdMob module&lt;/h2&gt;

&lt;p&gt;To build the export templates you need to clone the godot source code.&lt;/p&gt;

&lt;p&gt;Clone godot source &lt;a href=&quot;https://www.github.com/godotengine&quot;&gt;from github&lt;/a&gt; and
checkout the same version tag as the Godot version you are running, in my case
I am running ArchLinux and currently I have Godot version 3.1.2 installed i.e:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ git clone https://github.com/godotengine/godot.git
$ cd godot
$ git checkout 3.1.2-stable
$ cd ..
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Clone the AdMod github repo https://github.com/kloder-games/godot-admob&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ git clone https://github.com/kloder-games/godot-admob.git
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;then copy the &lt;code class=&quot;highlighter-rouge&quot;&gt;admob&lt;/code&gt; directory to &lt;code class=&quot;highlighter-rouge&quot;&gt;godot/modules&lt;/code&gt; directory.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cp -r godot-admob/admob godot/modules/
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h2 id=&quot;building&quot;&gt;Building&lt;/h2&gt;

&lt;h3 id=&quot;install-dependencies&quot;&gt;Install Dependencies&lt;/h3&gt;

&lt;p&gt;Godot is using the &lt;a href=&quot;https://scons.org/&quot;&gt;scons&lt;/a&gt; build system. Java-8 is needed
to build Android export templates.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo pacman -S jdk8-openjdk scons
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Android SDK and &lt;a href=&quot;https://developer.android.com/ndk/downloads&quot;&gt;Android NDK&lt;/a&gt;
are needed, download and unpack them. Android SDK is installed by installing
&lt;a href=&quot;https://developer.android.com/studio&quot;&gt;Android Studio&lt;/a&gt;. Then set the environment
variables &lt;code class=&quot;highlighter-rouge&quot;&gt;ANDROID_HOME&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;ANDROID_NDK_ROOT&lt;/code&gt; to where they are located&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ export ANDROID_HOME=/home/bjorn/android-sdk/
$ export ANDROID_NDK_ROOT=/home/bjorn/android-ndk-r21b/
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h3 id=&quot;build-godot-libraries-for-android&quot;&gt;Build Godot Libraries for Android&lt;/h3&gt;

&lt;p&gt;It should now be possible to start building the Godot libraries. This will take
a while. You need to specify platform, target and architecture.&lt;/p&gt;

&lt;p&gt;First specify the architectures to build for. Google Play Store requires that
64-bit architectures are supported so build at least the architectures
&lt;code class=&quot;highlighter-rouge&quot;&gt;armv7&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;arm64v8&lt;/code&gt;. If the application should be running in an emulator,
make sure to build &lt;code class=&quot;highlighter-rouge&quot;&gt;x86&lt;/code&gt; (in case the emulator is using that architecture,
which is recommeded for performace reasons).&lt;/p&gt;

&lt;p&gt;If you want to load the application on an Android phone without Play Store it
is easier if you also build &lt;code class=&quot;highlighter-rouge&quot;&gt;debug&lt;/code&gt; target (then you don’t need to sign with
your own key, it is enough with the default debug key provided with Android
SDK), but then debug versions of the Godot libraries need to be built.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cd godot
$ scons platform=android target=release android_arch=armv7
$ scons platform=android target=release android_arch=arm64v8
$ scons platform=android target=release android_arch=x86
$ scons platform=android target=debug android_arch=armv7
$ scons platform=android target=debug android_arch=arm64v8
$ scons platform=android target=debug android_arch=x86
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;When all builds are completed there should be 2 library files for each build,
&lt;code class=&quot;highlighter-rouge&quot;&gt;libc++_shared.so&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;libgodot_android.so&lt;/code&gt;, in
&lt;code class=&quot;highlighter-rouge&quot;&gt;godot/platform/android/java/libs/&amp;lt;target&amp;gt;/&amp;lt;arch&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If, for some reason, the builds need to be redone clean with:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ scons platform=android --clean
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h3 id=&quot;update-android-files&quot;&gt;Update Android files&lt;/h3&gt;

&lt;p&gt;Building libraries will also create &lt;code class=&quot;highlighter-rouge&quot;&gt;AndroidManifest.xml&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;build.gradle&lt;/code&gt; in
&lt;code class=&quot;highlighter-rouge&quot;&gt;platform/android/java&lt;/code&gt;. Those files need some changes before building
export templates.&lt;/p&gt;

&lt;p&gt;You need to add the application id for your app in Google AdMod in
&lt;code class=&quot;highlighter-rouge&quot;&gt;AndroidManifest.xml&lt;/code&gt;. Open &lt;code class=&quot;highlighter-rouge&quot;&gt;godot/platform/android/java/Manifest.xml&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;Replace:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;meta-data
    android:name=&quot;com.google.android.gms.ads.AD_MANAGER_APP&quot;
    android:value=&quot;true&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;With:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;meta-data
    android:name=&quot;com.google.android.gms.ads.APPLICATION_ID&quot;
    android:value=&quot;&amp;lt;your-application-id&amp;gt;&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Where &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;your-application-id&amp;gt;&lt;/code&gt; is replaced with the Application id from Google
AdMob see &lt;a href=&quot;#google-admob&quot;&gt;Google AdMob section&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In my case I also want to change &lt;code class=&quot;highlighter-rouge&quot;&gt;minSdkVersion&lt;/code&gt; to only support a bit newer
Android versions. Default this is set to 18 which is Android version 4.3 which
is from 2013. I update this to 22 which is Android 5.1 from 2015. Open
&lt;code class=&quot;highlighter-rouge&quot;&gt;godot/platform/android/java/build.gradle&lt;/code&gt; and set&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;minSdkVersion 22
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Note that if you rebuild the Android Godot libraries the files you changed here
will be overwritten and hence you need to update them again.&lt;/p&gt;

&lt;p&gt;This also means that you need to build separate export templates for every
application you have, since the Application Id is specified in
&lt;code class=&quot;highlighter-rouge&quot;&gt;AndroidManifest.xml&lt;/code&gt;. This is unfortunate but I haven’t yet found any other
way.&lt;/p&gt;

&lt;h4 id=&quot;upgrade-admob-sdk&quot;&gt;Upgrade AdMob SDK&lt;/h4&gt;

&lt;p&gt;This is an optional step and can be skipped.&lt;/p&gt;

&lt;p&gt;Current version of AdMob module uses AdMod SDK version 16.0.0. The latest
version is currently 19.1.0 so let’s try to use that.
Open &lt;code class=&quot;highlighter-rouge&quot;&gt;build.gradle&lt;/code&gt; and replace&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;compile ('com.google.android.gms:play-services-ads:16.0.0') { exclude group: 'com.android.support' }
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;with&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;compile ('com.google.android.gms:play-services-ads:19.1.0') { exclude group: 'androidx.core' }
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Then you also need to add the following to &lt;code class=&quot;highlighter-rouge&quot;&gt;packagingOptions&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;exclude 'META-INF/androidx.legacy_legacy-support-core-utils.version'
exclude 'META-INF/androidx.loader_loader.version'
exclude 'META-INF/androidx.localbroadcastmanager_localbroadcastmanager.version'
exclude 'META-INF/androidx.print_print.version'
exclude 'META-INF/androidx.documentfile_documentfile.version'
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h3 id=&quot;build-android-export-templates&quot;&gt;Build Android export templates&lt;/h3&gt;

&lt;p&gt;Now it’s time to build the export template &lt;code class=&quot;highlighter-rouge&quot;&gt;apk's&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cd platform/android/java/
$ ./gradlew build
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The result from this build step are the files &lt;code class=&quot;highlighter-rouge&quot;&gt;godot/bin/android_release.apk&lt;/code&gt;
and &lt;code class=&quot;highlighter-rouge&quot;&gt;godot/bin/android_debug.apk&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The export template build can be cleaned with&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ./gradlew clean
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h3 id=&quot;troubleshooting-build-problems&quot;&gt;Troubleshooting build problems&lt;/h3&gt;

&lt;p&gt;In ArchLinux the build may fail with&lt;/p&gt;
&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;* What went wrong:
Could not open terminal for stdout: could not get termcap entry
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;This is solved by setting the TERM environment like this:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ export TERM=xterm-color
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h1 id=&quot;adding-google-ads-to-the-android-game&quot;&gt;Adding Google Ads to the Android game&lt;/h1&gt;

&lt;p&gt;Time to add some code to the Godot project for interacting with AdMob SDK.&lt;/p&gt;

&lt;h2 id=&quot;showing-ads-in-godot&quot;&gt;Showing Ads in Godot&lt;/h2&gt;

&lt;p&gt;Showing the ads is simple. First some initialization and then load the ad.
This is an example for a banner:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;admob&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;null&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Engine&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;has_singleton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AdMob&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;admob&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Engine&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_singleton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AdMob&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;admob&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_instance_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;admob&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loadBanner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ca-app-pub-3940256099942544/6300978111&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Then you can show and hide the banner as you wish with:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;admob.showBanner()
admob.hideBanner()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Note that the code above will show test ads. The banner ad unit id is a
test banner from Google, make sure to replace it with the real ad unit id
(that was created in &lt;a href=&quot;#google-admob&quot;&gt;Google AdMob&lt;/a&gt;)  and also change to
&lt;code class=&quot;highlighter-rouge&quot;&gt;admob.init(true, get_instance_id())&lt;/code&gt; because the first argument is whether the
ad is real or test (true means it is a real ad).&lt;/p&gt;

&lt;h2 id=&quot;exporting-the-game&quot;&gt;Exporting the game&lt;/h2&gt;

&lt;p&gt;For instruction on how to export the game follow the procedure, with the
modifications mentioned below, described in &lt;a href=&quot;/notes/Create-Android-Game-Applications-from-Godot.html&quot;&gt;Create Android Game Applications from
Godot&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In addition to the linked description:
In &lt;code class=&quot;highlighter-rouge&quot;&gt;Custom package&lt;/code&gt; select the export template apk’s you just built
both for debug and release.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/godot_admob/7-admob-custom-package.png&quot; alt=&quot;alt attribute&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Make sure to enable &lt;code class=&quot;highlighter-rouge&quot;&gt;Internet&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;Access Network State&lt;/code&gt; in privilegies since
the AdMob module need them.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/godot_admob/8-admob-permissions.png&quot; alt=&quot;alt attribute&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For exporting to Play Store make sure to uncheck the &lt;code class=&quot;highlighter-rouge&quot;&gt;Export with Debug&lt;/code&gt;
checkbox.&lt;/p&gt;

&lt;h1 id=&quot;example&quot;&gt;Example&lt;/h1&gt;

&lt;p&gt;This is an example of a banner ad in the Godot sample game
&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.mtdeer.platformer2d&quot;&gt;Platformer&lt;/a&gt;.
The banner is only shown on the game menu and not when playing the game, hence,
the banner can cover the game navigation buttons.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/godot_admob/9-admob-example.png&quot; alt=&quot;alt attribute&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There is also an interstitial ad when exiting the game.&lt;/p&gt;

&lt;p&gt;You can install and try the sample game from
&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.mtdeer.platformer2d&quot;&gt;Google Play Store&lt;/a&gt;
and you can also find the source code on
&lt;a href=&quot;https://github.com/bhjortsberg/platformer&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;references&quot;&gt;References&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.godotengine.org/en/latest/development/compiling/compiling_for_android.html&quot;&gt;Compile for Android&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.godotengine.org/en/latest/getting_started/workflow/export/exporting_for_android.html&quot;&gt;Export to Android&lt;/a&gt;&lt;/p&gt;

</description>
        <pubDate>Sat, 16 May 2020 00:00:00 +0200</pubDate>
        <link>http://hjortsberg.org/notes/Support-Google-AdMob-ads-in-Godot-Android-games.html</link>
        <guid isPermaLink="true">http://hjortsberg.org/notes/Support-Google-AdMob-ads-in-Godot-Android-games.html</guid>
        
        <category>Android</category>
        
        <category>Godot</category>
        
        
      </item>
    
      <item>
        <title>Create Android Game Applications from Godot</title>
        <description>&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://godotengine.org&quot;&gt;Godot&lt;/a&gt; is an open source game development framework
that can be used to create games for desktop or mobile devices. This note
describe how to create applications for Linux and Android from an existing
Godot project.&lt;/p&gt;

&lt;h1 id=&quot;setting-up-godot-for-creating-native-application&quot;&gt;Setting up Godot for creating native application&lt;/h1&gt;

&lt;p&gt;A project in Godot can be “exported” to applications for different platforms.
To be able to export the application an export template for the target platform
is needed.&lt;/p&gt;

&lt;h2 id=&quot;installing-export-templates&quot;&gt;Installing export templates&lt;/h2&gt;

&lt;p&gt;In Godot menu go to Editor -&amp;gt; Manage Export Templates:&lt;/p&gt;

&lt;p&gt;In the dialog Export template manager click &lt;code class=&quot;highlighter-rouge&quot;&gt;Download&lt;/code&gt; to install export
templates for the current version, you then need to select a mirror to download
from in the next dialog. The export templates are around 400MB to download and
are installed in &lt;code class=&quot;highlighter-rouge&quot;&gt;$HOME/.local/share/godot/templates/&amp;lt;version&amp;gt;/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/export_godot/godot_export_template_manager.png&quot; alt=&quot;alt attribute&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;setting-up-android-export-template&quot;&gt;Setting up Android export template&lt;/h2&gt;

&lt;p&gt;Android SDK is needed for creating an Android application so setting up
Godot for exporting to Android require some paths to binaries that comes
with Android SDK.&lt;/p&gt;

&lt;p&gt;In Godot menu go to Editor -&amp;gt; Editor Settings and scroll down to
&lt;code class=&quot;highlighter-rouge&quot;&gt;Export/Android&lt;/code&gt; and add path to &lt;code class=&quot;highlighter-rouge&quot;&gt;adb&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;jarsigner&lt;/code&gt;. &lt;code class=&quot;highlighter-rouge&quot;&gt;adb&lt;/code&gt; comes with Android
SDK, &lt;code class=&quot;highlighter-rouge&quot;&gt;jarsigner&lt;/code&gt; comes with Java SDK and in my case they are installed in
&lt;code class=&quot;highlighter-rouge&quot;&gt;/usr/bin/adb&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;/usr/bin/jarsigner&lt;/code&gt;. Then add path to the debug keystore,
if you have worked with Android Studio and created an application, you should
already have a debug keystore in  &lt;code class=&quot;highlighter-rouge&quot;&gt;/home/&amp;lt;user&amp;gt;/.android/debug.keystore&lt;/code&gt;, add
the keystore user &lt;code class=&quot;highlighter-rouge&quot;&gt;androiddebugkey&lt;/code&gt; and keystore password &lt;code class=&quot;highlighter-rouge&quot;&gt;android&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/export_godot/godot_editor_setting_android.png&quot; alt=&quot;alt attribute&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;creating-android-applications-with-godot&quot;&gt;Creating Android applications with Godot&lt;/h1&gt;

&lt;h2 id=&quot;for-installation-on-local-device&quot;&gt;For installation on local device&lt;/h2&gt;

&lt;p&gt;When installing an application on the device you have in hand you can
manage with the simplest way of export, Godot will sign the app with the debug
key so you don’t need to create a signing key.&lt;/p&gt;

&lt;p&gt;Open the Godot project that should be exported to an android app.&lt;/p&gt;

&lt;p&gt;In Godot menu go to Project -&amp;gt; Export click Add… and select Android.&lt;/p&gt;

&lt;p&gt;In Export Path enter a Package Name including path to where the .apk should be
exported. If none is given the default is that the project directory is opened
in the export step.&lt;/p&gt;

&lt;p&gt;Under package, write a package name and check the singed checkbox.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/export_godot/godot_export_1.png&quot; alt=&quot;alt attribute&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Select a launcher icon, if there are any (otherwise there will be a default
Godot icon for the app).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/export_godot/godot_export_2.png&quot; alt=&quot;alt attribute&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Check the architectures&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/export_godot/godot_export_3.png&quot; alt=&quot;alt attribute&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Then you need to check Permissions needed for the application, like e.g.
&lt;code class=&quot;highlighter-rouge&quot;&gt;Access Network State&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;Internet&lt;/code&gt; in case your app need any persmissions.&lt;/p&gt;

&lt;p&gt;Click the button &lt;code class=&quot;highlighter-rouge&quot;&gt;Export Project&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Ensure that the path is correct and select a feasible name for the apk-file.&lt;/p&gt;

&lt;p&gt;Check “Export With Debug”&lt;/p&gt;

&lt;p&gt;Click “Save”&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/export_godot/godot_export_4.png&quot; alt=&quot;alt attribute&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The resulting apk-file can be installed on an Android device with (see my
&lt;a href=&quot;/notes/Installing-an-Android-application-(.apk)-file.html&quot;&gt;note on how to install an Android application apk-file&lt;/a&gt;):&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ adb install &amp;lt;apk-file&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;However, it is not possible to upload this apk to Google Play Store since it
contains debug info and is only signed with a debug key.&lt;/p&gt;

&lt;h2 id=&quot;for-publishing-on-google-play-store&quot;&gt;For publishing on Google Play Store&lt;/h2&gt;

&lt;p&gt;There are a couple of things needed to upload the application to Google Play
Store.&lt;/p&gt;

&lt;p&gt;Google Play Store requires 64-bit version of the application so make sure that
&lt;code class=&quot;highlighter-rouge&quot;&gt;Arm 64-v8a&lt;/code&gt; architecture is checked.&lt;/p&gt;

&lt;p&gt;The other thing is that you need to sign the application with a real signing
key, in &lt;a href=&quot;https://play.google.com/apps/publish&quot;&gt;Google Play Console&lt;/a&gt; this is
called Upload Key. Also the app cannot contain debug info so you need to use a
release build.&lt;/p&gt;

&lt;p&gt;Create a release keystore like this:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ keytool -v -genkey -v -keystore bhj.keystore -alias bhj-game -keyalg RSA -validity 10000
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The tool will ask for keystore password, then some certificate information
and finally asking you to enter a password for the key, in Godot (at least in
version 3.1.2) one have to set the same password for the key and for the
keystore.&lt;/p&gt;

&lt;p&gt;Save this keystore file in a safe place and remember the password you used for
the key and keystore. Any updates you will do to this application need to be
signed with the same key.&lt;/p&gt;

&lt;p&gt;You can list the keystore with&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ keytool -list --keystore bhj.keystore
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Then when exporting the application you should now scroll down to &lt;code class=&quot;highlighter-rouge&quot;&gt;KeyStore&lt;/code&gt; and
add the keystore file (including path) to &lt;code class=&quot;highlighter-rouge&quot;&gt;Release&lt;/code&gt; the key alias to
&lt;code class=&quot;highlighter-rouge&quot;&gt;Release User&lt;/code&gt; and the password (which is the same for the keystore and for the
key) to &lt;code class=&quot;highlighter-rouge&quot;&gt;Release password&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/export_godot/godot_export_5.png&quot; alt=&quot;alt attribute&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You should then be able to export the application using the procedure described
above with one exception. You need to uncheck &lt;code class=&quot;highlighter-rouge&quot;&gt;Export with Debug&lt;/code&gt; before
clicking the Save button since debug information is not allowed on apps in Play
Store for security reasons.&lt;/p&gt;

&lt;p&gt;The resulting apk-file should be possible to upload
to Google Play Store using Google Play Console. Read more about
&lt;a href=&quot;/notes/Publish-an-Android-Application-on-Google-Play-Store.html&quot;&gt;how to publish an Android application on Google Play
Store&lt;/a&gt;
in my earlier post.&lt;/p&gt;

&lt;h2 id=&quot;tweaking-android-details&quot;&gt;Tweaking Android details&lt;/h2&gt;

&lt;p&gt;Developers familiar with Android applications may wonder about Target SDK and
other things that can be specified in gradle build files. Unfortunately this will
require you to rebuild the export templates. For example changing
&lt;code class=&quot;highlighter-rouge&quot;&gt;minSdkVersion&lt;/code&gt; to a higher Android version will require a rebuild of the export
templates. Update: More on &lt;a href=&quot;/notes/Support-Google-AdMob-ads-in-Godot-Android-games.html&quot;&gt;building export template in this later
note&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;linux-windows-and-osx&quot;&gt;Linux, Windows and OSX&lt;/h1&gt;

&lt;p&gt;There are templates for exporting to a number of other platforms as well, of
which I have tested only exporting to Linux.
Export templates for Linux and the other target are installed when
doing the step &lt;a href=&quot;#installing-export-templates&quot;&gt;Installing export templates above&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Go to Project -&amp;gt; Export click &lt;code class=&quot;highlighter-rouge&quot;&gt;Add...&lt;/code&gt; in the dialog and select a target to
export to:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/export_godot/export_different_platforms.png&quot; alt=&quot;alt attribute&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The procedure is simpler when exporting applications to desktops (Linux,
Windows and OSX) than for mobile targets Android and iOS.&lt;/p&gt;

&lt;h1 id=&quot;references&quot;&gt;References&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.godotengine.org/en/latest/getting_started/workflow/export/exporting_for_android.html&quot;&gt;Godot Docs - Export to Android&lt;/a&gt;&lt;/p&gt;

</description>
        <pubDate>Mon, 11 May 2020 00:00:00 +0200</pubDate>
        <link>http://hjortsberg.org/notes/Create-Android-Game-Applications-from-Godot.html</link>
        <guid isPermaLink="true">http://hjortsberg.org/notes/Create-Android-Game-Applications-from-Godot.html</guid>
        
        <category>Android</category>
        
        <category>Godot</category>
        
        
      </item>
    
      <item>
        <title>Installing an Android application (.apk) file</title>
        <description>&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;Installing an Android application package (an apk-file) when developing an
android application should be a rather trivial task. However, there are a few
tweaks needed to make it work in Linux. This note describes how you install
Android apk-files on a device from a Linux host. I have followed this procedure
on ArchLinux and on Ubuntu.&lt;/p&gt;

&lt;p&gt;In order to proceed you need Android SDK installed on the Linux host.&lt;/p&gt;

&lt;h2 id=&quot;turn-on-developer-mode-on-your-android-phone&quot;&gt;Turn on Developer mode on your Android phone&lt;/h2&gt;

&lt;p&gt;You need to turn on developer mode and allow usb-debugging. The way this is
done may depend on the Android device used.&lt;/p&gt;

&lt;p&gt;On Motorola Moto e6 (running Android 9) you go to settings, then
&lt;code class=&quot;highlighter-rouge&quot;&gt;System, advanced -&amp;gt; About phone&lt;/code&gt; then hit &lt;code class=&quot;highlighter-rouge&quot;&gt;Build number&lt;/code&gt; multiple times.&lt;/p&gt;

&lt;p&gt;When developer mode is turned on there should be a new menu option in
&lt;code class=&quot;highlighter-rouge&quot;&gt;Settings -&amp;gt; system -&amp;gt; Developer options&lt;/code&gt; where you can scroll down to Debugging
and enable USB Debugging.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/android-apk/enable_usb_debugging.png&quot; alt=&quot;alt attribute&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;no-permissions-for-device&quot;&gt;No permissions for device&lt;/h2&gt;

&lt;p&gt;When you plug your device you can use &lt;code class=&quot;highlighter-rouge&quot;&gt;adb&lt;/code&gt; (available from Android SDK) to list
devices.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ adb devices -l
List of devices attached
ZE2222MG7R             no permissions; see [http://developer.android.com/tools/device.html] usb:2-2 transport_id:1

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;as seen there are no permission to install an application via &lt;code class=&quot;highlighter-rouge&quot;&gt;adb&lt;/code&gt; on the
connected device. This can be solved by adding a &lt;code class=&quot;highlighter-rouge&quot;&gt;udev&lt;/code&gt; rule.&lt;/p&gt;

&lt;h2 id=&quot;usb-device-id&quot;&gt;USB device id&lt;/h2&gt;

&lt;p&gt;To create the &lt;code class=&quot;highlighter-rouge&quot;&gt;udev&lt;/code&gt; rule the the USB Device id is needed so let’s list the
device id using &lt;code class=&quot;highlighter-rouge&quot;&gt;lsusb&lt;/code&gt; (this can also be found in &lt;code class=&quot;highlighter-rouge&quot;&gt;dmesg&lt;/code&gt; when the device is
connected)&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ lsusb
Bus 002 Device 006: ID 0e8d:201c MediaTek Inc. moto e6 play
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Here the vendor id is &lt;code class=&quot;highlighter-rouge&quot;&gt;0e8d&lt;/code&gt; and the product id is &lt;code class=&quot;highlighter-rouge&quot;&gt;201c&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;adding-udev-rule&quot;&gt;Adding udev rule&lt;/h2&gt;

&lt;p&gt;Create a rules file for Android in &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/udev/rules.d/51-android.rules&lt;/code&gt; and add
the line&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SUBSYSTEM==&quot;usb&quot;, ATTR{idVendor}==&quot;0e8d&quot;, ATTR{idProduct}==&quot;201c&quot;, MODE=&quot;0660&quot;, OWNER=&quot;bjorn&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Now reload udev&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo udevadm control --reload
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Disconnect and reconnect the device.&lt;/p&gt;

&lt;p&gt;Now list devices again&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ adb devices -l
List of devices attached
ZE2222MG7R             unauthorized usb:2-2 transport_id:6
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;There should be a pop-up dialog on the device asking you to allow USB debugging.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/android-apk/allow_usb_debugging.png&quot; alt=&quot;alt attribute&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Authorize and list devices again&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ adb devices -l
List of devices attached
ZE2222MG7R             device usb:2-2 product:bali_reteu model:moto_e6_play device:bali transport_id:7
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Now you should be allowed to install applications on the device using &lt;code class=&quot;highlighter-rouge&quot;&gt;adb&lt;/code&gt;&lt;/p&gt;

&lt;h2 id=&quot;installing-the-app&quot;&gt;Installing the app&lt;/h2&gt;

&lt;p&gt;Now you can install the app using Android Studio or via &lt;code class=&quot;highlighter-rouge&quot;&gt;adb&lt;/code&gt;.
In &lt;code class=&quot;highlighter-rouge&quot;&gt;adb&lt;/code&gt; you will probably need to allow installing test packages, so add the
&lt;code class=&quot;highlighter-rouge&quot;&gt;-t&lt;/code&gt; flag.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ adb install -t &amp;lt;package&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;If you already have the package installed and want to replace the existing, add
the &lt;code class=&quot;highlighter-rouge&quot;&gt;-r&lt;/code&gt; flag&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ adb install -t -r &amp;lt;package&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

</description>
        <pubDate>Sat, 22 Feb 2020 00:00:00 +0100</pubDate>
        <link>http://hjortsberg.org/notes/Installing-an-Android-application-(.apk)-file.html</link>
        <guid isPermaLink="true">http://hjortsberg.org/notes/Installing-an-Android-application-(.apk)-file.html</guid>
        
        <category>Android</category>
        
        
      </item>
    
      <item>
        <title>Writing a remote shell in Java</title>
        <description>&lt;p&gt;This note describes how to write a simple remote shell in Java.&lt;/p&gt;

&lt;h1 id=&quot;remote-shell&quot;&gt;Remote shell&lt;/h1&gt;

&lt;p&gt;A remote shell application obviously need to be able to run a command on the
host computer, it also need some kind of socket server in order for clients to
run commands remotely.&lt;/p&gt;

&lt;h2 id=&quot;executing-command&quot;&gt;Executing command&lt;/h2&gt;

&lt;p&gt;The application need to execute a command and grab its output. The output
should then be sent to the remote client.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Process command = Runtime.getRuntime().exec(commandStr);
BufferedReader reader = new BufferedReader(
                        new InputStreamReader(command.getInputStream()));
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;So this code will run the command in &lt;code class=&quot;highlighter-rouge&quot;&gt;commandStr&lt;/code&gt; and then get the &lt;code class=&quot;highlighter-rouge&quot;&gt;InputStream&lt;/code&gt;
from the &lt;code class=&quot;highlighter-rouge&quot;&gt;Process&lt;/code&gt; object (where input means input to the application, i.e. the
output from the command). A reader is created for the stream and will later be
used to grab the output from the command.&lt;/p&gt;

&lt;h2 id=&quot;reading-command-output&quot;&gt;Reading command output&lt;/h2&gt;

&lt;p&gt;To read the command output just use the reader created earlier:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;String line = &quot;&quot;;
while ((line = reader.readLine()) != null) {
    System.out.println(line);
}
command.waitFor();
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The loop will read all outputs from the command and terminates on end of line.
The &lt;code class=&quot;highlighter-rouge&quot;&gt;command.waitFor()&lt;/code&gt; waits for the command to exit.&lt;/p&gt;

&lt;h2 id=&quot;starting-the-server&quot;&gt;Starting the server&lt;/h2&gt;

&lt;p&gt;Start a server listening on &lt;code class=&quot;highlighter-rouge&quot;&gt;port&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ServerSocket server = new ServerSocket(port);
Socket socket = server.accept();
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;When a client connects the blocking &lt;code class=&quot;highlighter-rouge&quot;&gt;accept()&lt;/code&gt; call will return a &lt;code class=&quot;highlighter-rouge&quot;&gt;Socket&lt;/code&gt; which
is used for communicating with the client.&lt;/p&gt;

&lt;h2 id=&quot;writing-command-output-to-client&quot;&gt;Writing command output to client&lt;/h2&gt;

&lt;p&gt;Writing the output from the command to the client means writing the command
input stream to the socket output stream. We create an &lt;code class=&quot;highlighter-rouge&quot;&gt;OutputStream&lt;/code&gt; from the
socket:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// Write output to client
OutputStream output = socket.getOutputStream();
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Then write the command output to the socket output stream. Modifying the loop
above when reading command output:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;String line = &quot;&quot;;
while ((line = reader.readLine()) != null) {
    System.out.println(line);
    line += &quot;\n&quot;;
    output.write(line.getBytes());
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Now the simple case with a one-off command is complete. The application accepts
a connection, sends the command output to the client and exits. However, if we
run an interactive command, like for example &lt;code class=&quot;highlighter-rouge&quot;&gt;bash&lt;/code&gt; there is yet no way to write
input to the running command. That’s the next task to add.&lt;/p&gt;

&lt;h2 id=&quot;writing-client-input-to-an-executing-command&quot;&gt;Writing client input to an executing command&lt;/h2&gt;

&lt;p&gt;With the simple case done, its time to also read input from the connected client
and write to the command output stream. Get the input stream from the socket
(i.e. this is where the client command will be received)&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// Read from client
InputStream input = socket.getInputStream();
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Create a writer for the command stream&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;OutputStream commandStream = command.getOutputStream();
BufferedWriter writer = new BufferedWriter(
                             new OutputStreamWriter(commandStream));
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Then if the command is still running read from the client input stream and write
to the command ouput stream.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;byte[] buffer = new byte[1024];
while (command.isAlive() &amp;amp;&amp;amp;
    ((bytesRead = input.read(buffer)) != -1))
{
    buffer[bytesRead] = '\n';
    if (command.isAlive()) {
        writer.write(new String(buffer));
        writer.flush();
    } else {
        break;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The way this loop is constructed the loop will not exit until the command exits.
Reading the input stream is a blocking call, hence the other loop writing the
command output to the client will never be reached. So this loop is meant to be
run in a sepearate thread, in parallel with the loop writing to client output
stream. So put the above in a &lt;code class=&quot;highlighter-rouge&quot;&gt;Runnable&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;new Thread(new Runnable() {
    @Override
    public void run() {
        // read socket and write to command stream
    }
}).start()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h1 id=&quot;running-the-remote-shell&quot;&gt;Running the Remote Shell&lt;/h1&gt;

&lt;p&gt;The remote shell can now be run as a one-off command like this:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ java RemoteShell 3333 uptime
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;When connecting with a client the output of the command (here &lt;code class=&quot;highlighter-rouge&quot;&gt;uptime&lt;/code&gt;) will be
sent to the client and the application will exit:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ netcat localhost 3333
 17:00:29 up 7 days,  9:05,  1 user,  load average: 1.87, 1.51, 1.34
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;If the remote shell is given the parameter to start bash it is possible for
the client to write commands to the bash command i.e. basically run any
command. This is the real remote shell.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ java ExecCommand 3333 bash
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;On the client, note that there will be no prompt. Just type the command and
see its output.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ netcat localhost 3333
date
Thu Jan 16 17:14:09 CET 2020
uptime
 17:14:12 up 7 days,  9:19,  1 user,  load average: 1.76, 1.50, 1.45
ls -l /media	
total 4
drwxr-x---+ 2 root root 4096 May  2  2018 bjorn
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h1 id=&quot;github-source-code&quot;&gt;GitHub source code&lt;/h1&gt;

&lt;p&gt;The code for this small application can be found
&lt;a href=&quot;https://github.com/bhjortsberg/remote-shell.git&quot;&gt;here&lt;/a&gt;. Enjoy!&lt;/p&gt;

</description>
        <pubDate>Sun, 19 Jan 2020 00:00:00 +0100</pubDate>
        <link>http://hjortsberg.org/notes/Writing-a-remote-shell-in-Java.html</link>
        <guid isPermaLink="true">http://hjortsberg.org/notes/Writing-a-remote-shell-in-Java.html</guid>
        
        <category>programming</category>
        
        
      </item>
    
  </channel>
</rss>
